From d16c7669b4f23380a50c8dedfaf6d25f50ca0edd Mon Sep 17 00:00:00 2001 From: yaruwangway <69694322+yaruwangway@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:33:34 +0200 Subject: [PATCH 01/38] fix: `AttributeDistributionTotal` in event emit (#1097) * fix: emitted distribution events * docs: update changelog * fix: lint --------- Co-authored-by: MSalopek --- CHANGELOG.md | 2 ++ x/ccv/consumer/keeper/distribution.go | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c07c612136..7f6893544e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Add an entry to the unreleased section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a release. +* (fix) [#720](https://github.com/cosmos/interchain-security/issues/720) Fix the attribute `AttributeDistributionTotal` value in `FeeDistribution` event emit. + ## v3.0.0 Date: June 21st, 2023 diff --git a/x/ccv/consumer/keeper/distribution.go b/x/ccv/consumer/keeper/distribution.go index e2c8d2a403..9d59be2f89 100644 --- a/x/ccv/consumer/keeper/distribution.go +++ b/x/ccv/consumer/keeper/distribution.go @@ -113,10 +113,12 @@ func (k Keeper) SendRewardsToProvider(ctx sdk.Context) error { timeoutTimestamp := uint64(ctx.BlockTime().Add(transferTimeoutPeriod).UnixNano()) sentCoins := sdk.NewCoins() + var allBalances sdk.Coins // iterate over all whitelisted reward denoms for _, denom := range k.AllowedRewardDenoms(ctx) { // get the balance of the denom in the toSendToProviderTokens address balance := k.bankKeeper.GetBalance(ctx, tstProviderAddr, denom) + allBalances = allBalances.Add(balance) // if the balance is not zero, if !balance.IsZero() { @@ -138,11 +140,8 @@ func (k Keeper) SendRewardsToProvider(ctx sdk.Context) error { } } - consumerFeePoolAddr := k.authKeeper.GetModuleAccount(ctx, k.feeCollectorName).GetAddress() - fpTokens := k.bankKeeper.GetAllBalances(ctx, consumerFeePoolAddr) - k.Logger(ctx).Info("sent block rewards to provider", - "total fee pool", fpTokens.String(), + "total fee pool", allBalances.String(), "sent", sentCoins.String(), ) currentHeight := ctx.BlockHeight() @@ -153,7 +152,7 @@ func (k Keeper) SendRewardsToProvider(ctx sdk.Context) error { sdk.NewAttribute(ccv.AttributeDistributionCurrentHeight, strconv.Itoa(int(currentHeight))), sdk.NewAttribute(ccv.AttributeDistributionNextHeight, strconv.Itoa(int(currentHeight+k.GetBlocksPerDistributionTransmission(ctx)))), sdk.NewAttribute(ccv.AttributeDistributionFraction, (k.GetConsumerRedistributionFrac(ctx))), - sdk.NewAttribute(ccv.AttributeDistributionTotal, fpTokens.String()), + sdk.NewAttribute(ccv.AttributeDistributionTotal, allBalances.String()), sdk.NewAttribute(ccv.AttributeDistributionToProvider, sentCoins.String()), ), ) From 21b2650741c7d9310d88a5ec82dacc2b47415a3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 07:05:51 -0700 Subject: [PATCH 02/38] build(deps): bump github.com/cometbft/cometbft from 0.37.1 to 0.37.2 (#1116) Bumps [github.com/cometbft/cometbft](https://github.com/cometbft/cometbft) from 0.37.1 to 0.37.2. - [Release notes](https://github.com/cometbft/cometbft/releases) - [Changelog](https://github.com/cometbft/cometbft/blob/main/CHANGELOG.md) - [Commits](https://github.com/cometbft/cometbft/compare/v0.37.1...v0.37.2) --- updated-dependencies: - dependency-name: github.com/cometbft/cometbft dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b487f32ab9..036c4d4c46 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( cosmossdk.io/errors v1.0.0-beta.7 cosmossdk.io/math v1.0.1 - github.com/cometbft/cometbft v0.37.1 + github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.8.0 github.com/cosmos/cosmos-sdk v0.47.3 github.com/cosmos/gogoproto v1.4.10 diff --git a/go.sum b/go.sum index 6e22b529cf..6ef711918b 100644 --- a/go.sum +++ b/go.sum @@ -360,8 +360,8 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE github.com/coinbase/kryptology v1.8.0/go.mod h1:RYXOAPdzOGUe3qlSFkMGn58i3xUA8hmxYHksuq+8ciI= github.com/coinbase/rosetta-sdk-go v0.7.9 h1:lqllBjMnazTjIqYrOGv8h8jxjg9+hJazIGZr9ZvoCcA= github.com/coinbase/rosetta-sdk-go v0.7.9/go.mod h1:0/knutI7XGVqXmmH4OQD8OckFrbQ8yMsUZTG7FXCR2M= -github.com/cometbft/cometbft v0.37.1 h1:KLxkQTK2hICXYq21U2hn1W5hOVYUdQgDQ1uB+90xPIg= -github.com/cometbft/cometbft v0.37.1/go.mod h1:Y2MMMN//O5K4YKd8ze4r9jmk4Y7h0ajqILXbH5JQFVs= +github.com/cometbft/cometbft v0.37.2 h1:XB0yyHGT0lwmJlFmM4+rsRnczPlHoAKFX6K8Zgc2/Jc= +github.com/cometbft/cometbft v0.37.2/go.mod h1:Y2MMMN//O5K4YKd8ze4r9jmk4Y7h0ajqILXbH5JQFVs= github.com/cometbft/cometbft-db v0.8.0 h1:vUMDaH3ApkX8m0KZvOFFy9b5DZHBAjsnEuo9AKVZpjo= github.com/cometbft/cometbft-db v0.8.0/go.mod h1:6ASCP4pfhmrCBpfk01/9E1SI29nD3HfVHrY4PG8x5c0= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= From 7fcdf7ea6ae5048b4cd3dec7b7852871983bcc3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 07:38:23 -0700 Subject: [PATCH 03/38] build(deps): bump bufbuild/buf-setup-action from 1.22.0 to 1.23.1 (#1112) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.22.0 to 1.23.1. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.22.0...v1.23.1) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 483fab06c1..08748fe079 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.22.0 + - uses: bufbuild/buf-setup-action@v1.23.1 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 4f2b512e97..0049c05d20 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.22.0 + - uses: bufbuild/buf-setup-action@v1.23.1 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From 30f37f7de67a03df8cab95798584646eda53a626 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Mon, 10 Jul 2023 16:46:00 +0800 Subject: [PATCH 04/38] refactor: sort imports with gci (#1034) apply gci linter --- .golangci.yml | 13 ++- .../ante/forbidden_proposals_ante.go | 1 - .../ante/forbidden_proposals_ante_test.go | 7 +- app/consumer-democracy/ante_handler.go | 7 +- app/consumer-democracy/app.go | 94 +++++++++---------- app/consumer-democracy/export.go | 6 +- .../proposals_whitelisting.go | 3 +- .../proposals_whitelisting_test.go | 3 +- .../ante/disabled_modules_ante_test.go | 6 +- app/consumer/ante/msg_filter_ante_test.go | 6 +- app/consumer/ante_handler.go | 6 +- app/consumer/app.go | 42 ++++----- app/consumer/export.go | 6 +- app/provider/ante_handler.go | 6 +- app/provider/app.go | 47 +++++----- app/provider/export.go | 4 +- app/sovereign/ante_handler.go | 6 +- app/sovereign/app.go | 86 ++++++++--------- app/sovereign/export.go | 4 +- cmd/interchain-security-cd/cmd/root.go | 13 ++- cmd/interchain-security-cd/main.go | 1 + cmd/interchain-security-cdd/cmd/root.go | 10 +- cmd/interchain-security-cdd/main.go | 1 + cmd/interchain-security-pd/cmd/root.go | 11 ++- cmd/interchain-security-pd/main.go | 1 + cmd/interchain-security-sd/cmd/root.go | 11 ++- cmd/interchain-security-sd/main.go | 1 + tests/difference/core/driver/common.go | 5 +- tests/difference/core/driver/core_test.go | 14 ++- .../core/driver/seed_gen_fuzzy_test.go | 7 +- tests/difference/core/driver/setup.go | 34 ++++--- tests/e2e/actions.go | 5 +- tests/integration/common.go | 20 ++-- tests/integration/democracy.go | 10 +- tests/integration/distribution.go | 3 +- tests/integration/expired_client.go | 10 +- tests/integration/instance_test.go | 3 +- tests/integration/key_assignment.go | 8 +- tests/integration/normal_operations.go | 4 +- tests/integration/setup.go | 18 ++-- tests/integration/slashing.go | 12 ++- tests/integration/stop_consumer.go | 6 +- tests/integration/throttle.go | 6 +- tests/integration/unbonding.go | 1 + tests/integration/valset_update.go | 9 +- x/ccv/consumer/client/cli/query.go | 1 + x/ccv/consumer/ibc_module.go | 12 +-- x/ccv/consumer/ibc_module_test.go | 11 ++- x/ccv/consumer/keeper/changeover.go | 3 +- x/ccv/consumer/keeper/changeover_test.go | 9 +- x/ccv/consumer/keeper/distribution.go | 7 +- x/ccv/consumer/keeper/distribution_test.go | 5 +- x/ccv/consumer/keeper/genesis.go | 5 +- x/ccv/consumer/keeper/genesis_test.go | 20 ++-- x/ccv/consumer/keeper/grpc_query.go | 6 +- x/ccv/consumer/keeper/hooks.go | 1 + x/ccv/consumer/keeper/keeper.go | 15 +-- x/ccv/consumer/keeper/keeper_test.go | 14 +-- x/ccv/consumer/keeper/params_test.go | 3 +- x/ccv/consumer/keeper/relay.go | 11 ++- x/ccv/consumer/keeper/relay_test.go | 16 ++-- x/ccv/consumer/keeper/soft_opt_out.go | 1 + x/ccv/consumer/keeper/soft_opt_out_test.go | 4 +- x/ccv/consumer/keeper/validators.go | 6 +- x/ccv/consumer/keeper/validators_test.go | 12 ++- x/ccv/consumer/module.go | 9 +- x/ccv/consumer/types/genesis.go | 4 +- x/ccv/consumer/types/genesis_test.go | 12 +-- x/ccv/consumer/types/keys.go | 1 + x/ccv/consumer/types/params.go | 1 + x/ccv/consumer/types/validator.go | 1 + x/ccv/democracy/distribution/module.go | 8 +- x/ccv/democracy/governance/module.go | 4 +- x/ccv/democracy/staking/module.go | 3 +- x/ccv/provider/client/cli/tx.go | 2 +- x/ccv/provider/client/proposal_handler.go | 6 +- x/ccv/provider/handler.go | 2 + x/ccv/provider/handler_test.go | 4 +- x/ccv/provider/ibc_module.go | 11 ++- x/ccv/provider/ibc_module_test.go | 13 ++- x/ccv/provider/keeper/distribution.go | 1 + x/ccv/provider/keeper/distribution_test.go | 6 +- x/ccv/provider/keeper/genesis.go | 1 + x/ccv/provider/keeper/genesis_test.go | 9 +- x/ccv/provider/keeper/grpc_query.go | 7 +- x/ccv/provider/keeper/hooks.go | 2 +- x/ccv/provider/keeper/hooks_test.go | 4 +- x/ccv/provider/keeper/keeper.go | 17 ++-- x/ccv/provider/keeper/keeper_test.go | 7 +- x/ccv/provider/keeper/key_assignment.go | 7 +- x/ccv/provider/keeper/key_assignment_test.go | 12 ++- x/ccv/provider/keeper/msg_server.go | 6 +- x/ccv/provider/keeper/params.go | 4 +- x/ccv/provider/keeper/params_test.go | 5 +- x/ccv/provider/keeper/proposal.go | 12 ++- x/ccv/provider/keeper/proposal_test.go | 9 +- x/ccv/provider/keeper/relay.go | 8 +- x/ccv/provider/keeper/relay_test.go | 15 +-- x/ccv/provider/keeper/throttle.go | 4 +- x/ccv/provider/keeper/throttle_test.go | 9 +- x/ccv/provider/module.go | 12 +-- x/ccv/provider/module_test.go | 11 +-- x/ccv/provider/proposal_handler.go | 5 +- x/ccv/provider/proposal_handler_test.go | 9 +- x/ccv/provider/types/genesis.go | 4 +- x/ccv/provider/types/genesis_test.go | 11 ++- x/ccv/provider/types/key_assignment.go | 2 + x/ccv/provider/types/keys_test.go | 4 +- x/ccv/provider/types/params.go | 6 +- x/ccv/provider/types/params_test.go | 7 +- x/ccv/provider/types/proposal.go | 4 +- x/ccv/provider/types/proposal_test.go | 8 +- x/ccv/types/ccv.go | 4 +- x/ccv/types/ccv_test.go | 7 +- x/ccv/types/expected_keepers.go | 12 ++- x/ccv/types/shared_params.go | 3 +- x/ccv/types/utils.go | 13 ++- x/ccv/types/utils_test.go | 7 +- 118 files changed, 612 insertions(+), 482 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 430440a76f..5a3b555a6d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,9 +10,9 @@ run: linters: disable-all: true enable: - - depguard - dogsled - exportloopref + - gci - goconst - gocritic - gofumpt @@ -60,6 +60,17 @@ issues: max-same-issues: 10000 linters-settings: + gci: + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - blank # blank imports + - dot # dot imports + - prefix(cosmossdk.io) + - prefix(github.com/cosmos/cosmos-sdk) + - prefix(github.com/cometbft/cometbft) + - prefix(github.com/cosmos/interchain-security) + custom-order: true gofumpt: # Choose whether to use the extra rules. # Default: false diff --git a/app/consumer-democracy/ante/forbidden_proposals_ante.go b/app/consumer-democracy/ante/forbidden_proposals_ante.go index f8acfd8bc5..2856aa4e5b 100644 --- a/app/consumer-democracy/ante/forbidden_proposals_ante.go +++ b/app/consumer-democracy/ante/forbidden_proposals_ante.go @@ -4,7 +4,6 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" ) diff --git a/app/consumer-democracy/ante/forbidden_proposals_ante_test.go b/app/consumer-democracy/ante/forbidden_proposals_ante_test.go index bbe9f6a5dd..0b17e1a0b7 100644 --- a/app/consumer-democracy/ante/forbidden_proposals_ante_test.go +++ b/app/consumer-democracy/ante/forbidden_proposals_ante_test.go @@ -3,6 +3,9 @@ package ante_test import ( "testing" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -10,11 +13,9 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + app "github.com/cosmos/interchain-security/v3/app/consumer-democracy" "github.com/cosmos/interchain-security/v3/app/consumer-democracy/ante" - "github.com/stretchr/testify/require" - - ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) // in SDKv47 parameter updates full params object is required diff --git a/app/consumer-democracy/ante_handler.go b/app/consumer-democracy/ante_handler.go index e27986a482..eff1a5b1c7 100644 --- a/app/consumer-democracy/ante_handler.go +++ b/app/consumer-democracy/ante_handler.go @@ -1,12 +1,15 @@ package app import ( + ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + democracyante "github.com/cosmos/interchain-security/v3/app/consumer-democracy/ante" consumerante "github.com/cosmos/interchain-security/v3/app/consumer/ante" ibcconsumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index 6b3078212b..063c5bf427 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -7,23 +7,29 @@ import ( "os" "path/filepath" - "github.com/cosmos/cosmos-sdk/client/flags" - nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" - "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/x/consensus" - consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" - consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - appparams "github.com/cosmos/interchain-security/v3/app/params" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v7/modules/core" + ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/spf13/cast" autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" @@ -45,77 +51,67 @@ import ( authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" "github.com/cosmos/cosmos-sdk/x/bank" - - runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/consensus" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/evidence" evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" "github.com/cosmos/cosmos-sdk/x/feegrant" feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + gov "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + // add mint + mint "github.com/cosmos/cosmos-sdk/x/mint" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params" paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/upgrade" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" dbm "github.com/cometbft/cometbft-db" abci "github.com/cometbft/cometbft/abci/types" tmjson "github.com/cometbft/cometbft/libs/json" "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - "github.com/cosmos/cosmos-sdk/x/slashing" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/upgrade" - upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v7/modules/apps/transfer" - ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" - ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v7/modules/core" - ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" - ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + + appparams "github.com/cosmos/interchain-security/v3/app/params" ibctestingcore "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/core" ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" - "github.com/spf13/cast" - - distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" testutil "github.com/cosmos/interchain-security/v3/testutil/integration" - ccvdistr "github.com/cosmos/interchain-security/v3/x/ccv/democracy/distribution" - - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccvstaking "github.com/cosmos/interchain-security/v3/x/ccv/democracy/staking" - - gov "github.com/cosmos/cosmos-sdk/x/gov" - govclient "github.com/cosmos/cosmos-sdk/x/gov/client" - govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - ccvgov "github.com/cosmos/interchain-security/v3/x/ccv/democracy/governance" - - // add mint - mint "github.com/cosmos/cosmos-sdk/x/mint" - mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - - paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" - tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" consumer "github.com/cosmos/interchain-security/v3/x/ccv/consumer" consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + ccvdistr "github.com/cosmos/interchain-security/v3/x/ccv/democracy/distribution" + ccvgov "github.com/cosmos/interchain-security/v3/x/ccv/democracy/governance" + ccvstaking "github.com/cosmos/interchain-security/v3/x/ccv/democracy/staking" ) const ( diff --git a/app/consumer-democracy/export.go b/app/consumer-democracy/export.go index 6c511d6871..bb710da1c4 100644 --- a/app/consumer-democracy/export.go +++ b/app/consumer-democracy/export.go @@ -4,12 +4,12 @@ import ( "encoding/json" "fmt" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - - tmtypes "github.com/cometbft/cometbft/types" servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmtypes "github.com/cometbft/cometbft/types" ) // ExportAppStateAndValidators exports the state of the application for a genesis diff --git a/app/consumer-democracy/proposals_whitelisting.go b/app/consumer-democracy/proposals_whitelisting.go index ab9114343f..1e9141895c 100644 --- a/app/consumer-democracy/proposals_whitelisting.go +++ b/app/consumer-democracy/proposals_whitelisting.go @@ -1,9 +1,10 @@ package app import ( + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" "github.com/cosmos/cosmos-sdk/x/params/types/proposal" - ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) func IsProposalWhitelisted(content v1beta1.Content) bool { diff --git a/app/consumer-democracy/proposals_whitelisting_test.go b/app/consumer-democracy/proposals_whitelisting_test.go index a51709ae4a..127ec82ec5 100644 --- a/app/consumer-democracy/proposals_whitelisting_test.go +++ b/app/consumer-democracy/proposals_whitelisting_test.go @@ -3,10 +3,11 @@ package app_test import ( "testing" + "github.com/stretchr/testify/require" + appConsumer "github.com/cosmos/interchain-security/v3/app/consumer-democracy" ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" icstestingutils "github.com/cosmos/interchain-security/v3/testutil/ibc_testing" - "github.com/stretchr/testify/require" ) func TestDemocracyGovernanceWhitelistingKeys(t *testing.T) { diff --git a/app/consumer/ante/disabled_modules_ante_test.go b/app/consumer/ante/disabled_modules_ante_test.go index dd58e8b79e..1fa114f00c 100644 --- a/app/consumer/ante/disabled_modules_ante_test.go +++ b/app/consumer/ante/disabled_modules_ante_test.go @@ -3,14 +3,16 @@ package ante_test import ( "testing" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/interchain-security/v3/app/consumer/ante" "github.com/cosmos/interchain-security/v3/app/params" - "github.com/stretchr/testify/require" ) func TestDisabledModulesDecorator(t *testing.T) { diff --git a/app/consumer/ante/msg_filter_ante_test.go b/app/consumer/ante/msg_filter_ante_test.go index 76b3dec604..04bc838a8a 100644 --- a/app/consumer/ante/msg_filter_ante_test.go +++ b/app/consumer/ante/msg_filter_ante_test.go @@ -3,12 +3,14 @@ package ante_test import ( "testing" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/interchain-security/v3/app/consumer/ante" "github.com/cosmos/interchain-security/v3/app/params" - "github.com/stretchr/testify/require" ) type consumerKeeper struct { diff --git a/app/consumer/ante_handler.go b/app/consumer/ante_handler.go index f963150626..d251966685 100644 --- a/app/consumer/ante_handler.go +++ b/app/consumer/ante_handler.go @@ -1,12 +1,14 @@ package app import ( + ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" consumerante "github.com/cosmos/interchain-security/v3/app/consumer/ante" ibcconsumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" diff --git a/app/consumer/app.go b/app/consumer/app.go index 17c15c2c86..c65447e244 100644 --- a/app/consumer/app.go +++ b/app/consumer/app.go @@ -7,14 +7,20 @@ import ( "os" "path/filepath" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v7/modules/core" + ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/spf13/cast" + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" - dbm "github.com/cometbft/cometbft-db" - abci "github.com/cometbft/cometbft/abci/types" - tmjson "github.com/cometbft/cometbft/libs/json" - "github.com/cometbft/cometbft/libs/log" - tmos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -35,9 +41,6 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" @@ -63,6 +66,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/feegrant" feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/params" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" @@ -73,25 +78,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/upgrade" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v7/modules/apps/transfer" - ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" - ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v7/modules/core" - ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" - ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + tmjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/libs/log" + tmos "github.com/cometbft/cometbft/libs/os" + appparams "github.com/cosmos/interchain-security/v3/app/params" ibctestingcore "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/core" ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" - "github.com/spf13/cast" - - tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + testutil "github.com/cosmos/interchain-security/v3/testutil/integration" ibcconsumer "github.com/cosmos/interchain-security/v3/x/ccv/consumer" ibcconsumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" ibcconsumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" - - testutil "github.com/cosmos/interchain-security/v3/testutil/integration" ) const ( diff --git a/app/consumer/export.go b/app/consumer/export.go index 168e5385a1..4260efda92 100644 --- a/app/consumer/export.go +++ b/app/consumer/export.go @@ -4,12 +4,12 @@ import ( "encoding/json" "fmt" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - - tmtypes "github.com/cometbft/cometbft/types" servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmtypes "github.com/cometbft/cometbft/types" ) // ExportAppStateAndValidators implements the simapp app interface diff --git a/app/provider/ante_handler.go b/app/provider/ante_handler.go index 0a71573b07..9ba33d3264 100644 --- a/app/provider/ante_handler.go +++ b/app/provider/ante_handler.go @@ -1,12 +1,14 @@ package app import ( + ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC diff --git a/app/provider/app.go b/app/provider/app.go index d810ff360f..a25e3f4ab6 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -7,22 +7,33 @@ import ( "os" "path/filepath" - "github.com/cosmos/cosmos-sdk/client/flags" - nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" - "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v7/modules/core" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" + ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/spf13/cast" autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" - appparams "github.com/cosmos/interchain-security/v3/app/params" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" "github.com/cosmos/cosmos-sdk/codec" - runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" - "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" + "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" @@ -80,35 +91,21 @@ import ( upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v7/modules/apps/transfer" - ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" - ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v7/modules/core" - ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" - ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" - ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" - tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - - ibctestingcore "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/core" - ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" dbm "github.com/cometbft/cometbft-db" abci "github.com/cometbft/cometbft/abci/types" tmjson "github.com/cometbft/cometbft/libs/json" "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - "github.com/spf13/cast" + appparams "github.com/cosmos/interchain-security/v3/app/params" + ibctestingcore "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/core" + ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" + testutil "github.com/cosmos/interchain-security/v3/testutil/integration" ibcprovider "github.com/cosmos/interchain-security/v3/x/ccv/provider" ibcproviderclient "github.com/cosmos/interchain-security/v3/x/ccv/provider/client" ibcproviderkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - - testutil "github.com/cosmos/interchain-security/v3/testutil/integration" ) const ( diff --git a/app/provider/export.go b/app/provider/export.go index 3865026b01..74faed0657 100644 --- a/app/provider/export.go +++ b/app/provider/export.go @@ -4,13 +4,13 @@ import ( "encoding/json" "log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" ) // ExportAppStateAndValidators exports the state of the application for a genesis diff --git a/app/sovereign/ante_handler.go b/app/sovereign/ante_handler.go index 0a71573b07..9ba33d3264 100644 --- a/app/sovereign/ante_handler.go +++ b/app/sovereign/ante_handler.go @@ -1,12 +1,14 @@ package app import ( + ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC diff --git a/app/sovereign/app.go b/app/sovereign/app.go index b1049255f9..1693cb1b60 100644 --- a/app/sovereign/app.go +++ b/app/sovereign/app.go @@ -7,10 +7,19 @@ import ( "os" "path/filepath" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v7/modules/core" + ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/spf13/cast" + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" - runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" - appparams "github.com/cosmos/interchain-security/v3/app/params" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" @@ -20,8 +29,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/runtime" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" @@ -30,11 +39,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" - - "github.com/cosmos/cosmos-sdk/x/consensus" - consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" - consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" - "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/ante" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" @@ -51,70 +55,58 @@ import ( "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/consensus" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + sdkdistr "github.com/cosmos/cosmos-sdk/x/distribution" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/evidence" evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" "github.com/cosmos/cosmos-sdk/x/feegrant" feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + sdkgov "github.com/cosmos/cosmos-sdk/x/gov" govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - + // add mint + mint "github.com/cosmos/cosmos-sdk/x/mint" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params" paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + sdkstaking "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/upgrade" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" dbm "github.com/cometbft/cometbft-db" abci "github.com/cometbft/cometbft/abci/types" tmjson "github.com/cometbft/cometbft/libs/json" "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - "github.com/cosmos/cosmos-sdk/x/slashing" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/upgrade" - upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v7/modules/apps/transfer" - ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" - ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v7/modules/core" - ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" - ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + + appparams "github.com/cosmos/interchain-security/v3/app/params" ibctestingcore "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/core" ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" - "github.com/spf13/cast" - - sdkdistr "github.com/cosmos/cosmos-sdk/x/distribution" - distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" testutil "github.com/cosmos/interchain-security/v3/testutil/integration" - - sdkstaking "github.com/cosmos/cosmos-sdk/x/staking" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - - sdkgov "github.com/cosmos/cosmos-sdk/x/gov" - govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - - // add mint - mint "github.com/cosmos/cosmos-sdk/x/mint" - mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - - paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" - tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) const ( diff --git a/app/sovereign/export.go b/app/sovereign/export.go index 770d9b540f..93d46bf973 100644 --- a/app/sovereign/export.go +++ b/app/sovereign/export.go @@ -4,13 +4,13 @@ import ( "encoding/json" "log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" ) // ExportAppStateAndValidators exports the state of the application for a genesis diff --git a/cmd/interchain-security-cd/cmd/root.go b/cmd/interchain-security-cd/cmd/root.go index 5fac172a96..30c032bbea 100644 --- a/cmd/interchain-security-cd/cmd/root.go +++ b/cmd/interchain-security-cd/cmd/root.go @@ -5,15 +5,11 @@ import ( "io" "os" - dbm "github.com/cometbft/cometbft-db" - tmcfg "github.com/cometbft/cometbft/config" - "github.com/cometbft/cometbft/libs/log" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" "github.com/spf13/cobra" "github.com/spf13/viper" rosettaCmd "cosmossdk.io/tools/rosetta/cmd" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" "github.com/cosmos/cosmos-sdk/client/debug" @@ -22,12 +18,19 @@ import ( "github.com/cosmos/cosmos-sdk/client/pruning" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/server" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/crisis" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + + dbm "github.com/cometbft/cometbft-db" + tmcfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/libs/log" + consumer "github.com/cosmos/interchain-security/v3/app/consumer" "github.com/cosmos/interchain-security/v3/app/params" ) diff --git a/cmd/interchain-security-cd/main.go b/cmd/interchain-security-cd/main.go index 012e5c6d44..12afc46e65 100644 --- a/cmd/interchain-security-cd/main.go +++ b/cmd/interchain-security-cd/main.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + app "github.com/cosmos/interchain-security/v3/app/consumer" "github.com/cosmos/interchain-security/v3/cmd/interchain-security-cd/cmd" ) diff --git a/cmd/interchain-security-cdd/cmd/root.go b/cmd/interchain-security-cdd/cmd/root.go index 1e3f038717..989bb5a462 100644 --- a/cmd/interchain-security-cdd/cmd/root.go +++ b/cmd/interchain-security-cdd/cmd/root.go @@ -5,14 +5,11 @@ import ( "io" "os" - dbm "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" "github.com/spf13/cobra" "github.com/spf13/viper" rosettaCmd "cosmossdk.io/tools/rosetta/cmd" - tmcfg "github.com/cometbft/cometbft/config" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" "github.com/cosmos/cosmos-sdk/client/debug" @@ -21,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/pruning" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/server" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" @@ -29,6 +27,10 @@ import ( "github.com/cosmos/cosmos-sdk/x/crisis" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + dbm "github.com/cometbft/cometbft-db" + tmcfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/libs/log" + cdd "github.com/cosmos/interchain-security/v3/app/consumer-democracy" "github.com/cosmos/interchain-security/v3/app/params" ) diff --git a/cmd/interchain-security-cdd/main.go b/cmd/interchain-security-cdd/main.go index 5d04ea379b..085903425e 100644 --- a/cmd/interchain-security-cdd/main.go +++ b/cmd/interchain-security-cdd/main.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + app "github.com/cosmos/interchain-security/v3/app/consumer-democracy" "github.com/cosmos/interchain-security/v3/cmd/interchain-security-cdd/cmd" ) diff --git a/cmd/interchain-security-pd/cmd/root.go b/cmd/interchain-security-pd/cmd/root.go index 246e1c182b..745d81127c 100644 --- a/cmd/interchain-security-pd/cmd/root.go +++ b/cmd/interchain-security-pd/cmd/root.go @@ -5,14 +5,11 @@ import ( "io" "os" - dbm "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" "github.com/spf13/cobra" "github.com/spf13/viper" rosettaCmd "cosmossdk.io/tools/rosetta/cmd" - tmcfg "github.com/cometbft/cometbft/config" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" "github.com/cosmos/cosmos-sdk/client/debug" @@ -21,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/pruning" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/server" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" @@ -28,6 +26,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/crisis" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + + dbm "github.com/cometbft/cometbft-db" + tmcfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/libs/log" + "github.com/cosmos/interchain-security/v3/app/params" providerApp "github.com/cosmos/interchain-security/v3/app/provider" ) diff --git a/cmd/interchain-security-pd/main.go b/cmd/interchain-security-pd/main.go index 9ad1cd67cc..361c0fb8e1 100644 --- a/cmd/interchain-security-pd/main.go +++ b/cmd/interchain-security-pd/main.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + app "github.com/cosmos/interchain-security/v3/app/provider" "github.com/cosmos/interchain-security/v3/cmd/interchain-security-pd/cmd" ) diff --git a/cmd/interchain-security-sd/cmd/root.go b/cmd/interchain-security-sd/cmd/root.go index a93c841474..e5264b8735 100644 --- a/cmd/interchain-security-sd/cmd/root.go +++ b/cmd/interchain-security-sd/cmd/root.go @@ -5,14 +5,11 @@ import ( "io" "os" - dbm "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" "github.com/spf13/cobra" "github.com/spf13/viper" rosettaCmd "cosmossdk.io/tools/rosetta/cmd" - tmcfg "github.com/cometbft/cometbft/config" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" "github.com/cosmos/cosmos-sdk/client/debug" @@ -21,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/pruning" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/server" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" @@ -28,6 +26,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/crisis" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + + dbm "github.com/cometbft/cometbft-db" + tmcfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/libs/log" + "github.com/cosmos/interchain-security/v3/app/params" sovereignApp "github.com/cosmos/interchain-security/v3/app/sovereign" ) diff --git a/cmd/interchain-security-sd/main.go b/cmd/interchain-security-sd/main.go index 7c4dc35493..9b59188ea5 100644 --- a/cmd/interchain-security-sd/main.go +++ b/cmd/interchain-security-sd/main.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + app "github.com/cosmos/interchain-security/v3/app/sovereign" "github.com/cosmos/interchain-security/v3/cmd/interchain-security-sd/cmd" ) diff --git a/tests/difference/core/driver/common.go b/tests/difference/core/driver/common.go index bff271e235..8ea1a4cc36 100644 --- a/tests/difference/core/driver/common.go +++ b/tests/difference/core/driver/common.go @@ -3,10 +3,11 @@ package core import ( "time" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmtypes "github.com/cometbft/cometbft/types" ) const ( diff --git a/tests/difference/core/driver/core_test.go b/tests/difference/core/driver/core_test.go index 648955f66d..12192eb8e4 100644 --- a/tests/difference/core/driver/core_test.go +++ b/tests/difference/core/driver/core_test.go @@ -5,21 +5,19 @@ import ( "testing" "time" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/suite" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - ibctestingcore "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/core" - ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" + "github.com/stretchr/testify/suite" + sdk "github.com/cosmos/cosmos-sdk/types" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + appConsumer "github.com/cosmos/interchain-security/v3/app/consumer" appProvider "github.com/cosmos/interchain-security/v3/app/provider" - + ibctestingcore "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/core" + ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" simibc "github.com/cosmos/interchain-security/v3/testutil/simibc" - - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" ) diff --git a/tests/difference/core/driver/seed_gen_fuzzy_test.go b/tests/difference/core/driver/seed_gen_fuzzy_test.go index 0ea051c63b..610d31e40e 100644 --- a/tests/difference/core/driver/seed_gen_fuzzy_test.go +++ b/tests/difference/core/driver/seed_gen_fuzzy_test.go @@ -2,15 +2,16 @@ package core_test import ( "bytes" + cryptoEd25519 "crypto/ed25519" "testing" - cryptoEd25519 "crypto/ed25519" + mock "github.com/cosmos/ibc-go/v7/testing/mock" - tmtypes "github.com/cometbft/cometbft/types" cosmosEd25519 "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - mock "github.com/cosmos/ibc-go/v7/testing/mock" + + tmtypes "github.com/cometbft/cometbft/types" ) func GetPV(seed []byte) mock.PV { diff --git a/tests/difference/core/driver/setup.go b/tests/difference/core/driver/setup.go index ff42784bc5..fb5bf6a611 100644 --- a/tests/difference/core/driver/setup.go +++ b/tests/difference/core/driver/setup.go @@ -6,41 +6,39 @@ import ( "encoding/json" "time" - abci "github.com/cometbft/cometbft/abci/types" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v7/testing/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/cosmos/cosmos-sdk/baseapp" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cosmosEd25519 "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" - - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - "github.com/cosmos/ibc-go/v7/testing/mock" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmtypes "github.com/cometbft/cometbft/types" + appConsumer "github.com/cosmos/interchain-security/v3/app/consumer" appProvider "github.com/cosmos/interchain-security/v3/app/provider" + ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" icstestingutils "github.com/cosmos/interchain-security/v3/testutil/ibc_testing" simibc "github.com/cosmos/interchain-security/v3/testutil/simibc" consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" providerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" - ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 719e5d7517..3dd8fb704e 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -12,13 +12,14 @@ import ( "sync" "time" - evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/tidwall/gjson" + + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/client" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - "github.com/tidwall/gjson" ) type SendTokensAction struct { diff --git a/tests/integration/common.go b/tests/integration/common.go index d387760724..7d5175d5d7 100644 --- a/tests/integration/common.go +++ b/tests/integration/common.go @@ -4,24 +4,28 @@ import ( "fmt" "time" - "cosmossdk.io/math" - abci "github.com/cometbft/cometbft/abci/types" - tmtypes "github.com/cometbft/cometbft/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/stretchr/testify/require" + + "cosmossdk.io/math" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + tmtypes "github.com/cometbft/cometbft/types" + ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" icstestingutils "github.com/cosmos/interchain-security/v3/testutil/ibc_testing" testutil "github.com/cosmos/interchain-security/v3/testutil/integration" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/stretchr/testify/require" ) // ChainType defines the type of chain (either provider or consumer) diff --git a/tests/integration/democracy.go b/tests/integration/democracy.go index f914d581d7..461cfec3b3 100644 --- a/tests/integration/democracy.go +++ b/tests/integration/democracy.go @@ -4,18 +4,20 @@ import ( "testing" "time" + "github.com/stretchr/testify/suite" + "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" - ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" - icstestingutils "github.com/cosmos/interchain-security/v3/testutil/ibc_testing" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + + ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" + icstestingutils "github.com/cosmos/interchain-security/v3/testutil/ibc_testing" testutil "github.com/cosmos/interchain-security/v3/testutil/integration" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" - "github.com/stretchr/testify/suite" ) type ConsumerDemocracyTestSuite struct { diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index 8ba2a8756b..a896a6f22b 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -3,10 +3,11 @@ package integration import ( "strings" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" diff --git a/tests/integration/expired_client.go b/tests/integration/expired_client.go index 89c8280b33..38d2a3ab5e 100644 --- a/tests/integration/expired_client.go +++ b/tests/integration/expired_client.go @@ -3,14 +3,16 @@ package integration import ( "time" - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/tests/integration/instance_test.go b/tests/integration/instance_test.go index 7602f02c5e..3c2f018807 100644 --- a/tests/integration/instance_test.go +++ b/tests/integration/instance_test.go @@ -3,12 +3,13 @@ package integration_test import ( "testing" + "github.com/stretchr/testify/suite" + appConsumer "github.com/cosmos/interchain-security/v3/app/consumer" appConsumerDemocracy "github.com/cosmos/interchain-security/v3/app/consumer-democracy" appProvider "github.com/cosmos/interchain-security/v3/app/provider" intg "github.com/cosmos/interchain-security/v3/tests/integration" icstestingutils "github.com/cosmos/interchain-security/v3/testutil/ibc_testing" - "github.com/stretchr/testify/suite" ) // This file can be used as an example integration testing instance for any provider/consumer applications. diff --git a/tests/integration/key_assignment.go b/tests/integration/key_assignment.go index 5ffabbeab4..fbe6430c67 100644 --- a/tests/integration/key_assignment.go +++ b/tests/integration/key_assignment.go @@ -1,11 +1,13 @@ package integration import ( - tmencoding "github.com/cometbft/cometbft/crypto/encoding" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/cosmos/ibc-go/v7/testing/mock" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/ibc-go/v7/testing/mock" + + tmencoding "github.com/cometbft/cometbft/crypto/encoding" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" providerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" diff --git a/tests/integration/normal_operations.go b/tests/integration/normal_operations.go index af2137e5dc..b676689e89 100644 --- a/tests/integration/normal_operations.go +++ b/tests/integration/normal_operations.go @@ -1,8 +1,10 @@ package integration import ( - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ) diff --git a/tests/integration/setup.go b/tests/integration/setup.go index 07987ce0d7..a18ff551e4 100644 --- a/tests/integration/setup.go +++ b/tests/integration/setup.go @@ -3,21 +3,21 @@ package integration import ( "testing" - tmencoding "github.com/cometbft/cometbft/crypto/encoding" - sdk "github.com/cosmos/cosmos-sdk/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v7/testing/mock" - testutil "github.com/cosmos/interchain-security/v3/testutil/integration" + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" + + tmencoding "github.com/cometbft/cometbft/crypto/encoding" + ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" icstestingutils "github.com/cosmos/interchain-security/v3/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v3/testutil/integration" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - ibctesting "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/testing" - - "github.com/stretchr/testify/suite" ) // Callback for instantiating a new coordinator with a provider test chains diff --git a/tests/integration/slashing.go b/tests/integration/slashing.go index 68b632efe1..28ae9da958 100644 --- a/tests/integration/slashing.go +++ b/tests/integration/slashing.go @@ -4,20 +4,22 @@ import ( "fmt" "time" - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/crypto/ed25519" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto/ed25519" tmtypes "github.com/cometbft/cometbft/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + keepertestutil "github.com/cosmos/interchain-security/v3/testutil/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) // TestRelayAndApplyDowntimePacket tests that downtime slash packets can be properly relayed diff --git a/tests/integration/stop_consumer.go b/tests/integration/stop_consumer.go index f6e0d77d59..43ecaf9e33 100644 --- a/tests/integration/stop_consumer.go +++ b/tests/integration/stop_consumer.go @@ -1,10 +1,12 @@ package integration import ( - abci "github.com/cometbft/cometbft/abci/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + + abci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" diff --git a/tests/integration/throttle.go b/tests/integration/throttle.go index 0620bdd6b6..0505d6257a 100644 --- a/tests/integration/throttle.go +++ b/tests/integration/throttle.go @@ -3,10 +3,12 @@ package integration import ( "time" - tmtypes "github.com/cometbft/cometbft/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + + tmtypes "github.com/cometbft/cometbft/types" icstestingutils "github.com/cosmos/interchain-security/v3/testutil/ibc_testing" "github.com/cosmos/interchain-security/v3/x/ccv/provider" diff --git a/tests/integration/unbonding.go b/tests/integration/unbonding.go index 87454be324..7693c782e5 100644 --- a/tests/integration/unbonding.go +++ b/tests/integration/unbonding.go @@ -4,6 +4,7 @@ import ( "time" "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" providerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" diff --git a/tests/integration/valset_update.go b/tests/integration/valset_update.go index e1c0d920ea..955261aa79 100644 --- a/tests/integration/valset_update.go +++ b/tests/integration/valset_update.go @@ -3,11 +3,14 @@ package integration import ( "time" - abci "github.com/cometbft/cometbft/abci/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + abci "github.com/cometbft/cometbft/abci/types" + ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/consumer/client/cli/query.go b/x/ccv/consumer/client/cli/query.go index dc88aaf9d9..a961b9642c 100644 --- a/x/ccv/consumer/client/cli/query.go +++ b/x/ccv/consumer/client/cli/query.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ) diff --git a/x/ccv/consumer/ibc_module.go b/x/ccv/consumer/ibc_module.go index 0ca0b29370..74c4cff27c 100644 --- a/x/ccv/consumer/ibc_module.go +++ b/x/ccv/consumer/ibc_module.go @@ -4,18 +4,18 @@ import ( "fmt" "strings" - errorsmod "cosmossdk.io/errors" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" diff --git a/x/ccv/consumer/ibc_module_test.go b/x/ccv/consumer/ibc_module_test.go index 9e326bcbe6..a451625230 100644 --- a/x/ccv/consumer/ibc_module_test.go +++ b/x/ccv/consumer/ibc_module_test.go @@ -3,20 +3,21 @@ package consumer_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/consumer" consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" ) // TestOnChanOpenInit validates the consumer's OnChanOpenInit implementation against the spec. diff --git a/x/ccv/consumer/keeper/changeover.go b/x/ccv/consumer/keeper/changeover.go index 1221792d3c..74276d1253 100644 --- a/x/ccv/consumer/keeper/changeover.go +++ b/x/ccv/consumer/keeper/changeover.go @@ -1,8 +1,9 @@ package keeper import ( - abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" + + abci "github.com/cometbft/cometbft/abci/types" ) // ChangeoverIsComplete returns whether the standalone to consumer changeover process is complete. diff --git a/x/ccv/consumer/keeper/changeover_test.go b/x/ccv/consumer/keeper/changeover_test.go index 4009890b52..fcd9a70459 100644 --- a/x/ccv/consumer/keeper/changeover_test.go +++ b/x/ccv/consumer/keeper/changeover_test.go @@ -3,13 +3,16 @@ package keeper_test import ( "testing" - abci "github.com/cometbft/cometbft/abci/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + sdkcryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/interchain-security/v3/testutil/crypto" uthelpers "github.com/cosmos/interchain-security/v3/testutil/keeper" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" ) func TestChangeoverToConsumer(t *testing.T) { diff --git a/x/ccv/consumer/keeper/distribution.go b/x/ccv/consumer/keeper/distribution.go index 9d59be2f89..48f1c5a1eb 100644 --- a/x/ccv/consumer/keeper/distribution.go +++ b/x/ccv/consumer/keeper/distribution.go @@ -4,13 +4,14 @@ import ( "fmt" "strconv" - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/consumer/keeper/distribution_test.go b/x/ccv/consumer/keeper/distribution_test.go index 07f49b5b8c..71df5fd93d 100644 --- a/x/ccv/consumer/keeper/distribution_test.go +++ b/x/ccv/consumer/keeper/distribution_test.go @@ -4,13 +4,14 @@ import ( "strings" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" + testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" - "github.com/golang/mock/gomock" ) // TestGetEstimatedNextFeeDistribution tests next fee distribution parameters. diff --git a/x/ccv/consumer/keeper/genesis.go b/x/ccv/consumer/keeper/genesis.go index d9e905bbd8..45ea54f49c 100644 --- a/x/ccv/consumer/keeper/genesis.go +++ b/x/ccv/consumer/keeper/genesis.go @@ -4,10 +4,11 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" abci "github.com/cometbft/cometbft/abci/types" + + consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) // InitGenesis initializes the CCV consumer state and binds to PortID. diff --git a/x/ccv/consumer/keeper/genesis_test.go b/x/ccv/consumer/keeper/genesis_test.go index a2c0f6a88b..47d834f8a8 100644 --- a/x/ccv/consumer/keeper/genesis_test.go +++ b/x/ccv/consumer/keeper/genesis_test.go @@ -4,25 +4,25 @@ import ( "testing" "time" - abci "github.com/cometbft/cometbft/abci/types" - tmtypes "github.com/cometbft/cometbft/types" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - "github.com/cosmos/interchain-security/v3/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" - consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" - ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + tmtypes "github.com/cometbft/cometbft/types" + + "github.com/cosmos/interchain-security/v3/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) // TestInitGenesis tests that a consumer chain is correctly initialised from genesis. diff --git a/x/ccv/consumer/keeper/grpc_query.go b/x/ccv/consumer/keeper/grpc_query.go index 299e485faf..174f591497 100644 --- a/x/ccv/consumer/keeper/grpc_query.go +++ b/x/ccv/consumer/keeper/grpc_query.go @@ -3,10 +3,12 @@ package keeper import ( "context" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ) var _ types.QueryServer = Keeper{} diff --git a/x/ccv/consumer/keeper/hooks.go b/x/ccv/consumer/keeper/hooks.go index da3058555b..7f03896e55 100644 --- a/x/ccv/consumer/keeper/hooks.go +++ b/x/ccv/consumer/keeper/hooks.go @@ -2,6 +2,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index 24a1234065..7da13ab433 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -6,21 +6,22 @@ import ( "reflect" "time" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" - tmtypes "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index 7b23c777c5..662a776cef 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -6,21 +6,21 @@ import ( "testing" "time" - abci "github.com/cometbft/cometbft/abci/types" + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" ) // TestProviderClientID tests getter and setter functionality for the client ID stored on consumer keeper diff --git a/x/ccv/consumer/keeper/params_test.go b/x/ccv/consumer/keeper/params_test.go index 7573613199..ac6b112aa8 100644 --- a/x/ccv/consumer/keeper/params_test.go +++ b/x/ccv/consumer/keeper/params_test.go @@ -4,10 +4,11 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/stretchr/testify/require" ) // TestParams tests getters/setters for consumer params diff --git a/x/ccv/consumer/keeper/relay.go b/x/ccv/consumer/keeper/relay.go index d9e922ed3d..cd5105421e 100644 --- a/x/ccv/consumer/keeper/relay.go +++ b/x/ccv/consumer/keeper/relay.go @@ -4,14 +4,17 @@ import ( "fmt" "strconv" - errorsmod "cosmossdk.io/errors" - abci "github.com/cometbft/cometbft/abci/types" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/consumer/keeper/relay_test.go b/x/ccv/consumer/keeper/relay_test.go index 1c2ba5152f..962fc943bd 100644 --- a/x/ccv/consumer/keeper/relay_test.go +++ b/x/ccv/consumer/keeper/relay_test.go @@ -6,24 +6,24 @@ import ( "testing" "time" - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/libs/bytes" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/bytes" "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/types" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" ) // TestOnRecvVSCPacket tests the behavior of OnRecvVSCPacket over various packet scenarios diff --git a/x/ccv/consumer/keeper/soft_opt_out.go b/x/ccv/consumer/keeper/soft_opt_out.go index 23a27d4b96..09b8a9be79 100644 --- a/x/ccv/consumer/keeper/soft_opt_out.go +++ b/x/ccv/consumer/keeper/soft_opt_out.go @@ -5,6 +5,7 @@ import ( "sort" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ) diff --git a/x/ccv/consumer/keeper/soft_opt_out_test.go b/x/ccv/consumer/keeper/soft_opt_out_test.go index 34e5570674..c99d418ca6 100644 --- a/x/ccv/consumer/keeper/soft_opt_out_test.go +++ b/x/ccv/consumer/keeper/soft_opt_out_test.go @@ -3,13 +3,13 @@ package keeper_test import ( "testing" + "github.com/stretchr/testify/require" + tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" - - "github.com/stretchr/testify/require" ) // Tests that UpdateSmallestNonOptOutPower updates the smallest validator power that cannot soft opt out. diff --git a/x/ccv/consumer/keeper/validators.go b/x/ccv/consumer/keeper/validators.go index 068767546b..2233b22d28 100644 --- a/x/ccv/consumer/keeper/validators.go +++ b/x/ccv/consumer/keeper/validators.go @@ -4,11 +4,13 @@ import ( "time" "cosmossdk.io/math" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - abci "github.com/cometbft/cometbft/abci/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ) diff --git a/x/ccv/consumer/keeper/validators_test.go b/x/ccv/consumer/keeper/validators_test.go index 724ce787d0..67ab91f531 100644 --- a/x/ccv/consumer/keeper/validators_test.go +++ b/x/ccv/consumer/keeper/validators_test.go @@ -3,18 +3,22 @@ package keeper_test import ( "testing" + "github.com/stretchr/testify/require" + "cosmossdk.io/math" - abci "github.com/cometbft/cometbft/abci/types" - tmrand "github.com/cometbft/cometbft/libs/rand" - tmtypes "github.com/cometbft/cometbft/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + tmrand "github.com/cometbft/cometbft/libs/rand" + tmtypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" - "github.com/stretchr/testify/require" ) // TestApplyCCValidatorChanges tests the ApplyCCValidatorChanges method for a consumer keeper diff --git a/x/ccv/consumer/module.go b/x/ccv/consumer/module.go index 3d80fb1df1..6c05273451 100644 --- a/x/ccv/consumer/module.go +++ b/x/ccv/consumer/module.go @@ -5,9 +5,7 @@ import ( "encoding/json" "fmt" - abci "github.com/cometbft/cometbft/abci/types" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" @@ -17,11 +15,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + + abci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/client/cli" "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ) diff --git a/x/ccv/consumer/types/genesis.go b/x/ccv/consumer/types/genesis.go index 82b6e2c1fa..3a8769939b 100644 --- a/x/ccv/consumer/types/genesis.go +++ b/x/ccv/consumer/types/genesis.go @@ -1,9 +1,11 @@ package types import ( + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + errorsmod "cosmossdk.io/errors" + abci "github.com/cometbft/cometbft/abci/types" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/consumer/types/genesis_test.go b/x/ccv/consumer/types/genesis_test.go index 9ce1c4927a..2f4b2fa504 100644 --- a/x/ccv/consumer/types/genesis_test.go +++ b/x/ccv/consumer/types/genesis_test.go @@ -4,22 +4,20 @@ import ( "testing" "time" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - abci "github.com/cometbft/cometbft/abci/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/stretchr/testify/require" - "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/interchain-security/v3/testutil/crypto" - + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/stretchr/testify/require" ) const ( diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index 24be1819c6..0ceed2814e 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -5,6 +5,7 @@ import ( time "time" sdk "github.com/cosmos/cosmos-sdk/types" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/consumer/types/params.go b/x/ccv/consumer/types/params.go index 485bb8fda5..a9b4e61285 100644 --- a/x/ccv/consumer/types/params.go +++ b/x/ccv/consumer/types/params.go @@ -7,6 +7,7 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/consumer/types/validator.go b/x/ccv/consumer/types/validator.go index c546a68440..b134dde078 100644 --- a/x/ccv/consumer/types/validator.go +++ b/x/ccv/consumer/types/validator.go @@ -2,6 +2,7 @@ package types import ( errorsmod "cosmossdk.io/errors" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" diff --git a/x/ccv/democracy/distribution/module.go b/x/ccv/democracy/distribution/module.go index 7f4842314e..60532dde48 100644 --- a/x/ccv/democracy/distribution/module.go +++ b/x/ccv/democracy/distribution/module.go @@ -4,19 +4,19 @@ import ( "time" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution/exported" "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" abci "github.com/cometbft/cometbft/abci/types" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + + consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ) var ( diff --git a/x/ccv/democracy/governance/module.go b/x/ccv/democracy/governance/module.go index c293a9e3e3..3922607766 100644 --- a/x/ccv/democracy/governance/module.go +++ b/x/ccv/democracy/governance/module.go @@ -6,13 +6,13 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - - abci "github.com/cometbft/cometbft/abci/types" gov "github.com/cosmos/cosmos-sdk/x/gov" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + + abci "github.com/cometbft/cometbft/abci/types" ) const ( diff --git a/x/ccv/democracy/staking/module.go b/x/ccv/democracy/staking/module.go index 78489a0166..3e176aaa2c 100644 --- a/x/ccv/democracy/staking/module.go +++ b/x/ccv/democracy/staking/module.go @@ -3,7 +3,6 @@ package staking import ( "encoding/json" - abci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -11,6 +10,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/exported" "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" ) // Note: for a democracy consumer, this "democracy staking" keeper is only for governance capabilities, diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index ca167dbb60..7e0609d74c 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -9,9 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ) diff --git a/x/ccv/provider/client/proposal_handler.go b/x/ccv/provider/client/proposal_handler.go index 9c02dd3c23..8ec6e89bfd 100644 --- a/x/ccv/provider/client/proposal_handler.go +++ b/x/ccv/provider/client/proposal_handler.go @@ -8,6 +8,9 @@ import ( "path/filepath" "time" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" @@ -16,9 +19,8 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - "github.com/spf13/cobra" ) var ( diff --git a/x/ccv/provider/handler.go b/x/ccv/provider/handler.go index 6f3bb29092..71da622fce 100644 --- a/x/ccv/provider/handler.go +++ b/x/ccv/provider/handler.go @@ -2,8 +2,10 @@ package provider import ( errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ) diff --git a/x/ccv/provider/handler_test.go b/x/ccv/provider/handler_test.go index 8871d15669..e4835cf14a 100644 --- a/x/ccv/provider/handler_test.go +++ b/x/ccv/provider/handler_test.go @@ -5,13 +5,15 @@ import ( "strings" "testing" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + testcrypto "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/provider" diff --git a/x/ccv/provider/ibc_module.go b/x/ccv/provider/ibc_module.go index 698d37385b..efe113f895 100644 --- a/x/ccv/provider/ibc_module.go +++ b/x/ccv/provider/ibc_module.go @@ -3,16 +3,17 @@ package provider import ( "fmt" - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" diff --git a/x/ccv/provider/ibc_module_test.go b/x/ccv/provider/ibc_module_test.go index 96e0eb97d1..8b7c3f985c 100644 --- a/x/ccv/provider/ibc_module_test.go +++ b/x/ccv/provider/ibc_module_test.go @@ -3,24 +3,23 @@ package provider_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/provider" providerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" ) // TestOnChanOpenInit tests the provider's OnChanOpenInit method against spec. diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index ee6070e27b..a1dff6faf4 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -2,6 +2,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ) diff --git a/x/ccv/provider/keeper/distribution_test.go b/x/ccv/provider/keeper/distribution_test.go index 4188eedaef..a470feea74 100644 --- a/x/ccv/provider/keeper/distribution_test.go +++ b/x/ccv/provider/keeper/distribution_test.go @@ -3,12 +3,14 @@ package keeper_test import ( "testing" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" + testutil "github.com/cosmos/interchain-security/v3/testutil/keeper" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" ) // TestRegisterConsumerRewardDenom tests the RegisterConsumerRewardDenom method. diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index 996b73d4a5..b3a0a1ef04 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -4,6 +4,7 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/keeper/genesis_test.go b/x/ccv/provider/keeper/genesis_test.go index fe666dcc2c..d9147ce98f 100644 --- a/x/ccv/provider/keeper/genesis_test.go +++ b/x/ccv/provider/keeper/genesis_test.go @@ -4,17 +4,18 @@ import ( "testing" "time" - sdk "github.com/cosmos/cosmos-sdk/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" - consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" ) // TestInitAndExportGenesis tests the export and the initialisation of a provider chain genesis diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index f43761fd0e..2b522ea9ef 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -4,12 +4,15 @@ import ( "context" "fmt" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) var _ types.QueryServer = Keeper{} diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 2f2cb8ee0b..35c9b96301 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -1,9 +1,9 @@ package keeper import ( + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - sdk "github.com/cosmos/cosmos-sdk/types" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index 463ed5fff5..83dbfe9622 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -3,11 +3,13 @@ package keeper_test import ( "testing" + "github.com/golang/mock/gomock" + sdk "github.com/cosmos/cosmos-sdk/types" + cryptotestutil "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" providerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" - "github.com/golang/mock/gomock" ) func TestValidatorConsensusKeyInUse(t *testing.T) { diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 03caf0aba5..73785f1c17 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -6,25 +6,26 @@ import ( "reflect" "time" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" - ibchost "github.com/cosmos/ibc-go/v7/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/cometbft/cometbft/libs/log" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - - "github.com/cometbft/cometbft/libs/log" ) // Keeper defines the Cross-Chain Validation Provider Keeper diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index f34bf262a5..364aac5669 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -6,17 +6,18 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" ibcsimapp "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/simapp" cryptotestutil "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - - "github.com/stretchr/testify/require" ) const consumer = "consumer" diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index eb4fecd64f..d440848bbf 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -4,14 +4,15 @@ import ( "fmt" errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" - abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) // GetValidatorConsumerPubKey returns a validator's public key assigned for a consumer chain diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 49ef8698cb..8da633a5c9 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -7,18 +7,20 @@ import ( "testing" "time" - abci "github.com/cometbft/cometbft/abci/types" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + cryptotestutil "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" - "github.com/stretchr/testify/require" - providerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/golang/mock/gomock" ) func TestValidatorConsumerPubKeyCRUD(t *testing.T) { diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index d7051cc392..195e2a7215 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -4,11 +4,13 @@ import ( "context" "encoding/base64" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - errorsmod "cosmossdk.io/errors" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/keeper/params.go b/x/ccv/provider/keeper/params.go index adfc574ba9..b7b53b245b 100644 --- a/x/ccv/provider/keeper/params.go +++ b/x/ccv/provider/keeper/params.go @@ -3,10 +3,10 @@ package keeper import ( "time" - sdk "github.com/cosmos/cosmos-sdk/types" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/keeper/params_test.go b/x/ccv/provider/keeper/params_test.go index cfaec35dc7..05b3964bb7 100644 --- a/x/ccv/provider/keeper/params_test.go +++ b/x/ccv/provider/keeper/params_test.go @@ -4,14 +4,15 @@ import ( "testing" "time" - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - "github.com/stretchr/testify/require" ) // TestParams tests the getting/setting of provider ccv module params. diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 0ce71d6efc..11909ad685 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -5,17 +5,19 @@ import ( "strconv" "time" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" errorsmod "cosmossdk.io/errors" - abci "github.com/cometbft/cometbft/abci/types" - tmtypes "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + + abci "github.com/cometbft/cometbft/abci/types" + tmtypes "github.com/cometbft/cometbft/types" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 1170a0f368..4e9ee3f4ea 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -7,16 +7,17 @@ import ( "testing" "time" - abci "github.com/cometbft/cometbft/abci/types" - sdk "github.com/cosmos/cosmos-sdk/types" - evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" _go "github.com/cosmos/ics23/go" "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + + abci "github.com/cometbft/cometbft/abci/types" + cryptoutil "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 350bc968d4..df4fdb98ce 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -4,13 +4,15 @@ import ( "fmt" "strconv" - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 15305d8eea..b266211a42 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -4,13 +4,17 @@ import ( "testing" "time" - abci "github.com/cometbft/cometbft/abci/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" exported "github.com/cosmos/ibc-go/v7/modules/core/exported" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" ibcsimapp "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/simapp" cryptotestutil "github.com/cosmos/interchain-security/v3/testutil/crypto" @@ -18,9 +22,6 @@ import ( "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/golang/mock/gomock" - - "github.com/stretchr/testify/require" ) // TestQueueVSCPackets tests queueing validator set updates. diff --git a/x/ccv/provider/keeper/throttle.go b/x/ccv/provider/keeper/throttle.go index ddfe1b1763..d8be629ee7 100644 --- a/x/ccv/provider/keeper/throttle.go +++ b/x/ccv/provider/keeper/throttle.go @@ -5,9 +5,11 @@ import ( "time" "cosmossdk.io/math" - tmtypes "github.com/cometbft/cometbft/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + tmtypes "github.com/cometbft/cometbft/types" + providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/keeper/throttle_test.go b/x/ccv/provider/keeper/throttle_test.go index f923c53e98..a5356b0dc0 100644 --- a/x/ccv/provider/keeper/throttle_test.go +++ b/x/ccv/provider/keeper/throttle_test.go @@ -5,14 +5,15 @@ import ( "testing" "time" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" + "cosmossdk.io/math" - tmtypes "github.com/cometbft/cometbft/types" sdktypes "github.com/cosmos/cosmos-sdk/types" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - "golang.org/x/exp/slices" + tmtypes "github.com/cometbft/cometbft/types" cryptoutil "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index 556cbcfefa..82891c27c7 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -5,23 +5,23 @@ import ( "encoding/json" "fmt" - abci "github.com/cometbft/cometbft/abci/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + abci "github.com/cometbft/cometbft/abci/types" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/client/cli" "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/spf13/cobra" ) var ( diff --git a/x/ccv/provider/module_test.go b/x/ccv/provider/module_test.go index 0eee81cc1f..f32cb8cc4d 100644 --- a/x/ccv/provider/module_test.go +++ b/x/ccv/provider/module_test.go @@ -3,19 +3,18 @@ package provider_test import ( "testing" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" - testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/provider" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/golang/mock/gomock" ) // Tests the provider's InitGenesis implementation against the spec. diff --git a/x/ccv/provider/proposal_handler.go b/x/ccv/provider/proposal_handler.go index ebebb9c967..137cbecec7 100644 --- a/x/ccv/provider/proposal_handler.go +++ b/x/ccv/provider/proposal_handler.go @@ -1,11 +1,12 @@ package provider import ( - govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ) diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index a85853a246..d3707d8c28 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -4,14 +4,13 @@ import ( "testing" "time" - sdk "github.com/cosmos/cosmos-sdk/types" - distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - + sdk "github.com/cosmos/cosmos-sdk/types" + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" diff --git a/x/ccv/provider/types/genesis.go b/x/ccv/provider/types/genesis.go index 5a9358e736..07c135ebfc 100644 --- a/x/ccv/provider/types/genesis.go +++ b/x/ccv/provider/types/genesis.go @@ -3,9 +3,11 @@ package types import ( "fmt" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index de5c77a803..dbff6a1c2f 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -4,19 +4,20 @@ import ( "testing" "time" - abci "github.com/cometbft/cometbft/abci/types" - tmtypes "github.com/cometbft/cometbft/types" - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + abci "github.com/cometbft/cometbft/abci/types" + tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/interchain-security/v3/testutil/crypto" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" - - "github.com/stretchr/testify/require" ) // Tests validation of consumer states and params within a provider genesis state diff --git a/x/ccv/provider/types/key_assignment.go b/x/ccv/provider/types/key_assignment.go index 659ca91e83..78e5161a55 100644 --- a/x/ccv/provider/types/key_assignment.go +++ b/x/ccv/provider/types/key_assignment.go @@ -5,7 +5,9 @@ import ( "strings" errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index 3bb891ddce..9f470f4a82 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -4,10 +4,12 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" + cryptoutil "github.com/cosmos/interchain-security/v3/testutil/crypto" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - "github.com/stretchr/testify/require" ) // Tests that all singular keys, or prefixes to fully resolves keys are non duplicate byte values. diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index 0495d1a89e..515d8954ec 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -4,13 +4,13 @@ import ( "fmt" "time" - sdk "github.com/cosmos/cosmos-sdk/types" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/types/params_test.go b/x/ccv/provider/types/params_test.go index 7a4b9aabf2..0676a15986 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -4,12 +4,13 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ) diff --git a/x/ccv/provider/types/proposal.go b/x/ccv/provider/types/proposal.go index 89e8cb9822..c369ec95f1 100644 --- a/x/ccv/provider/types/proposal.go +++ b/x/ccv/provider/types/proposal.go @@ -6,10 +6,12 @@ import ( "strings" time "time" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + errorsmod "cosmossdk.io/errors" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) diff --git a/x/ccv/provider/types/proposal_test.go b/x/ccv/provider/types/proposal_test.go index b3f27e1415..fac4c7fc4b 100644 --- a/x/ccv/provider/types/proposal_test.go +++ b/x/ccv/provider/types/proposal_test.go @@ -5,19 +5,17 @@ import ( "testing" "time" - evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" - + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "github.com/golang/protobuf/proto" //nolint:staticcheck // see: https://github.com/cosmos/interchain-security/issues/236 "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ) diff --git a/x/ccv/types/ccv.go b/x/ccv/types/ccv.go index e7739b4c2f..f02ed1c450 100644 --- a/x/ccv/types/ccv.go +++ b/x/ccv/types/ccv.go @@ -4,8 +4,10 @@ import ( "fmt" errorsmod "cosmossdk.io/errors" - abci "github.com/cometbft/cometbft/abci/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" ) func NewValidatorSetChangePacketData(valUpdates []abci.ValidatorUpdate, valUpdateID uint64, slashAcks []string) ValidatorSetChangePacketData { diff --git a/x/ccv/types/ccv_test.go b/x/ccv/types/ccv_test.go index 5aadafe6ec..596967b199 100644 --- a/x/ccv/types/ccv_test.go +++ b/x/ccv/types/ccv_test.go @@ -3,11 +3,14 @@ package types_test import ( "testing" - abci "github.com/cometbft/cometbft/abci/types" + "github.com/stretchr/testify/require" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/stretchr/testify/require" ) func TestPacketDataValidateBasic(t *testing.T) { diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index 8176d193b2..2a82561cf0 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -4,18 +4,20 @@ import ( context "context" "time" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" auth "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" abci "github.com/cometbft/cometbft/abci/types" ) diff --git a/x/ccv/types/shared_params.go b/x/ccv/types/shared_params.go index 8ff676bded..566737c0b9 100644 --- a/x/ccv/types/shared_params.go +++ b/x/ccv/types/shared_params.go @@ -4,8 +4,9 @@ import ( fmt "fmt" "time" - sdktypes "github.com/cosmos/cosmos-sdk/types" ibchost "github.com/cosmos/ibc-go/v7/modules/core/24-host" + + sdktypes "github.com/cosmos/cosmos-sdk/types" ) const ( diff --git a/x/ccv/types/utils.go b/x/ccv/types/utils.go index 524725a532..f6e7ffc3aa 100644 --- a/x/ccv/types/utils.go +++ b/x/ccv/types/utils.go @@ -5,14 +5,17 @@ import ( "sort" "time" - errorsmod "cosmossdk.io/errors" - abci "github.com/cometbft/cometbft/abci/types" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + + errorsmod "cosmossdk.io/errors" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + abci "github.com/cometbft/cometbft/abci/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" ) func AccumulateChanges(currentChanges, newChanges []abci.ValidatorUpdate) []abci.ValidatorUpdate { diff --git a/x/ccv/types/utils_test.go b/x/ccv/types/utils_test.go index 0182b68306..f43e5449f1 100644 --- a/x/ccv/types/utils_test.go +++ b/x/ccv/types/utils_test.go @@ -3,11 +3,14 @@ package types_test import ( "testing" - abci "github.com/cometbft/cometbft/abci/types" + "github.com/stretchr/testify/require" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + + abci "github.com/cometbft/cometbft/abci/types" + ibcsimapp "github.com/cosmos/interchain-security/v3/legacy_ibc_testing/simapp" "github.com/cosmos/interchain-security/v3/x/ccv/types" - "github.com/stretchr/testify/require" ) func TestAccumulateChanges(t *testing.T) { From a72735bbb96598985541ba80b4b4d9e14acddbe8 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Mon, 10 Jul 2023 12:36:14 +0200 Subject: [PATCH 05/38] chore: add cometbft bump PR info to changelog (#1121) * chore: add cometbft bump PR info to changelog * chore: change styling in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f6893544e..92dc3b5efb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Add an entry to the unreleased section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a release. * (fix) [#720](https://github.com/cosmos/interchain-security/issues/720) Fix the attribute `AttributeDistributionTotal` value in `FeeDistribution` event emit. +* (deps) [#1119](https://github.com/cosmos/interchain-security/pull/1119) bump cometbft from `v0.37.1` to `0.37.2`. ## v3.0.0 From 14129233594bd72e57fa5ac286c79d6c420cce2a Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Wed, 12 Jul 2023 07:59:13 -0700 Subject: [PATCH 06/38] feat: remove consumer panic when ccv channel is closed (#1127) * rm panic * rm tests * Update CHANGELOG.md * update comment --- CHANGELOG.md | 8 ++++++++ tests/integration/stop_consumer.go | 29 ----------------------------- testutil/integration/debug_test.go | 4 ---- x/ccv/consumer/module.go | 5 +---- 4 files changed, 9 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92dc3b5efb..b984d03c08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ Add an entry to the unreleased section whenever merging a PR to main that is not * (fix) [#720](https://github.com/cosmos/interchain-security/issues/720) Fix the attribute `AttributeDistributionTotal` value in `FeeDistribution` event emit. * (deps) [#1119](https://github.com/cosmos/interchain-security/pull/1119) bump cometbft from `v0.37.1` to `0.37.2`. +## v3.1.0 + +Date July 11th, 2023 + +A minor upgrade to v3.0.0, which removes the panic in the consumer ccv module which would occur in an emergency scenario where the ccv channel is closed. + +* (feat) [#1127](https://github.com/cosmos/interchain-security/pull/1127) Remove consumer panic when ccv channel is closed + ## v3.0.0 Date: June 21st, 2023 diff --git a/tests/integration/stop_consumer.go b/tests/integration/stop_consumer.go index 43ecaf9e33..b3d32ee6a4 100644 --- a/tests/integration/stop_consumer.go +++ b/tests/integration/stop_consumer.go @@ -6,8 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) @@ -213,30 +211,3 @@ func (s *CCVTestSuite) checkConsumerChainIsRemoved(chainID string, checkChannel s.Require().Empty(slashData) s.Require().Empty(vscMaturedData) } - -// TestProviderChannelClosed checks that a consumer chain panics -// when the provider channel was established and then closed -func (suite *CCVTestSuite) TestProviderChannelClosed() { - suite.SetupCCVChannel(suite.path) - // establish provider channel with a first VSC packet - suite.SendEmptyVSCPacket() - - consumerKeeper := suite.consumerApp.GetConsumerKeeper() - - channelID, found := consumerKeeper.GetProviderChannel(suite.consumerChain.GetContext()) - suite.Require().True(found) - - // close provider channel - err := consumerKeeper.ChanCloseInit(suite.consumerChain.GetContext(), ccv.ConsumerPortID, channelID) - suite.Require().NoError(err) - suite.Require().True(consumerKeeper.IsChannelClosed(suite.consumerChain.GetContext(), channelID)) - - // assert begin blocker did panics - defer func() { - if r := recover(); r != nil { - return - } - suite.Require().Fail("Begin blocker did not panic with a closed channel") - }() - suite.consumerApp.BeginBlocker(suite.consumerChain.GetContext(), abci.RequestBeginBlock{}) -} diff --git a/testutil/integration/debug_test.go b/testutil/integration/debug_test.go index 3d04c540ee..077f33cde3 100644 --- a/testutil/integration/debug_test.go +++ b/testutil/integration/debug_test.go @@ -157,10 +157,6 @@ func TestStopConsumerOnChannelClosed(t *testing.T) { runCCVTestByName(t, "TestStopConsumerOnChannelClosed") } -func TestProviderChannelClosed(t *testing.T) { - runCCVTestByName(t, "TestProviderChannelClosed") -} - // // Throttle tests // diff --git a/x/ccv/consumer/module.go b/x/ccv/consumer/module.go index 6c05273451..fe9b18a945 100644 --- a/x/ccv/consumer/module.go +++ b/x/ccv/consumer/module.go @@ -143,12 +143,9 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { channelID, found := am.keeper.GetProviderChannel(ctx) if found && am.keeper.IsChannelClosed(ctx, channelID) { // The CCV channel was established, but it was then closed; - // the consumer chain is no longer safe, thus it MUST shut down. - // This is achieved by panicking, similar as it's done in the - // x/upgrade module of cosmos-sdk. + // the consumer chain is not secured anymore, but we allow it to run as a POA chain and log an error. channelClosedMsg := fmt.Sprintf("CCV channel %q was closed - shutdown consumer chain since it is not secured anymore", channelID) am.keeper.Logger(ctx).Error(channelClosedMsg) - panic(channelClosedMsg) } // map next block height to the vscID of the current block height From 08e59a85c958ad5020bc241d93d0c4ec7a712970 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Wed, 12 Jul 2023 10:37:24 -0700 Subject: [PATCH 07/38] docs: clean changelog for v3.1.0 (#1138) Update CHANGELOG.md --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b984d03c08..276edd4d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,15 @@ Add an entry to the unreleased section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a release. -* (fix) [#720](https://github.com/cosmos/interchain-security/issues/720) Fix the attribute `AttributeDistributionTotal` value in `FeeDistribution` event emit. -* (deps) [#1119](https://github.com/cosmos/interchain-security/pull/1119) bump cometbft from `v0.37.1` to `0.37.2`. - ## v3.1.0 Date July 11th, 2023 -A minor upgrade to v3.0.0, which removes the panic in the consumer ccv module which would occur in an emergency scenario where the ccv channel is closed. +A minor upgrade to v3.0.0, which removes the panic in the consumer ccv module which would occur in an emergency scenario where the ccv channel is closed. This release also fixes how a distribution related event is emitted, and bumps cometbft. * (feat) [#1127](https://github.com/cosmos/interchain-security/pull/1127) Remove consumer panic when ccv channel is closed +* (fix) [#720](https://github.com/cosmos/interchain-security/issues/720) Fix the attribute `AttributeDistributionTotal` value in `FeeDistribution` event emit. +* (deps) [#1119](https://github.com/cosmos/interchain-security/pull/1119) bump cometbft from `v0.37.1` to `0.37.2`. ## v3.0.0 From fd76f45b726f4ef65f817e8032c4f87f986f2d71 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Thu, 13 Jul 2023 15:09:29 +0200 Subject: [PATCH 08/38] docs: update broken md links (#1130) --- docs/docs/features/proposals.md | 2 +- docs/docs/validators/joining-testnet.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/docs/features/proposals.md b/docs/docs/features/proposals.md index a34160ecf4..b68fd343d6 100644 --- a/docs/docs/features/proposals.md +++ b/docs/docs/features/proposals.md @@ -48,7 +48,7 @@ Minimal example: "distribution_transmission_channel": "channel-123" } ``` -More examples can be found in the replicated security testnet repository [here](https://github.com/cosmos/testnets/blob/master/replicated-security/baryon-1/proposal-baryon-1.json) and [here](https://github.com/cosmos/testnets/blob/master/replicated-security/noble-1/start-proposal-noble-1.json). +More examples can be found in the replicated security testnet repository [here](https://github.com/cosmos/testnets/blob/master/replicated-security/stopped/baryon-1/proposal-baryon-1.json) and [here](https://github.com/cosmos/testnets/blob/master/replicated-security/stopped/noble-1/start-proposal-noble-1.json). ## `ConsumerRemovalProposal` Proposal type used to suggest removing an existing consumer chain. diff --git a/docs/docs/validators/joining-testnet.md b/docs/docs/validators/joining-testnet.md index 0e99c3710b..5ae9007416 100644 --- a/docs/docs/validators/joining-testnet.md +++ b/docs/docs/validators/joining-testnet.md @@ -180,9 +180,7 @@ gaiad tx provider assign-consensus-key consumer-1 '' --from Date: Thu, 13 Jul 2023 11:18:53 -0700 Subject: [PATCH 09/38] feat!: optimize pending packets storage on consumer + migration (#1037) * wip * tests * tests * update genesis tests * comments * migration and changelog * migration test * lints * merge fixes * clean * Update ccv.pb.go * add to ADR * address some PR comments * comment * Update ccv.pb.go * lint * Update x/ccv/consumer/keeper/keeper.go Co-authored-by: Simon Noetzlin * byte wise --------- Co-authored-by: Simon Noetzlin --- CHANGELOG.md | 2 + docs/docs/adrs/adr-008-throttle-retries.md | 16 +++ proto/interchain_security/ccv/v1/ccv.proto | 5 +- tests/integration/expired_client.go | 5 +- tests/integration/slashing.go | 24 ++-- x/ccv/consumer/keeper/genesis.go | 16 ++- x/ccv/consumer/keeper/genesis_test.go | 27 +++- x/ccv/consumer/keeper/keeper.go | 88 ++++++++----- x/ccv/consumer/keeper/keeper_test.go | 84 +++++++----- x/ccv/consumer/keeper/migration.go | 70 ++++++++++ x/ccv/consumer/keeper/migration_test.go | 68 ++++++++++ x/ccv/consumer/keeper/relay.go | 22 ++-- x/ccv/consumer/keeper/relay_test.go | 8 +- x/ccv/consumer/keeper/validators_test.go | 4 +- x/ccv/consumer/types/keys.go | 22 +++- x/ccv/consumer/types/keys_test.go | 4 +- x/ccv/types/ccv.go | 14 ++ x/ccv/types/ccv.pb.go | 145 +++++++++++++-------- 18 files changed, 465 insertions(+), 159 deletions(-) create mode 100644 x/ccv/consumer/keeper/migration.go create mode 100644 x/ccv/consumer/keeper/migration_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 276edd4d12..1c017fc5b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Add an entry to the unreleased section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a release. +* (feat!) optimize pending packets storage on consumer, with migration! [#1037](https://github.com/cosmos/interchain-security/pull/1037) + ## v3.1.0 Date July 11th, 2023 diff --git a/docs/docs/adrs/adr-008-throttle-retries.md b/docs/docs/adrs/adr-008-throttle-retries.md index a8f0d250ce..134214fffb 100644 --- a/docs/docs/adrs/adr-008-throttle-retries.md +++ b/docs/docs/adrs/adr-008-throttle-retries.md @@ -8,6 +8,7 @@ title: Throttle with retries ## Changelog * 6/9/23: Initial draft +* 6/22/23: added note on consumer pending packets storage optimization ## Status @@ -46,6 +47,21 @@ With the behavior described, we maintain very similar behavior to the current th In the normal case, when no or a few slash packets are being sent, the VSCMaturedPackets will not be delayed, and hence unbonding will not be delayed. +### Consumer pending packets storage optimization + +In addition to the mentioned consumer changes above. An optimization will need to be made to the consumer's pending packets storage to properly implement the feature from this ADR. + +The consumer ccv module previously queued "pending packets" to be sent on each endblocker in [SendPackets](https://github.com/cosmos/interchain-security/blob/3bc4e7135066d848aac60b0787364c07157fd36d/x/ccv/consumer/keeper/relay.go#L178). These packets are queued in state with a protobuf list of `ConsumerPacketData`. For a single append operation, the entire list is deserialized, then a packet is appended to that list, and the list is serialized again. See older version of [AppendPendingPacket](https://github.com/cosmos/interchain-security/blob/05c2dae7c6372b1252b9e97215d07c6aa7618f33/x/ccv/consumer/keeper/keeper.go#L606). That is, a single append operation has O(N) complexity, where N is the size of the list. + +This poor append performance isn't a problem when the pending packets list is small. But with this ADR being implemented, the pending packets list could potentially grow to the order of thousands of entries, in the scenario that a slash packet is bouncing. + +We can improve the append time for this queue by converting it from a protobuf-esq list, to a queue implemented with sdk-esq code. The idea is to persist an uint64 index that will be incremented each time you queue up a packet. You can think of this as storing the tail of the queue. Then, packet data will be keyed by that index, making the data naturally ordered byte-wise for sdk's iterator. The index will also be stored in the packet data value bytes, so that the index can later be used to delete certain packets from the queue. + +Two things are achieved with this approach: + +* More efficient packet append/enqueue times +* The ability to delete select packets from the queue (previously all packets were deleted at once) + ### Provider changes The main change needed for the provider is the removal of queuing logic for slash and vsc matured packets upon being received. diff --git a/proto/interchain_security/ccv/v1/ccv.proto b/proto/interchain_security/ccv/v1/ccv.proto index 1f3722a234..b8ad8ee6d0 100644 --- a/proto/interchain_security/ccv/v1/ccv.proto +++ b/proto/interchain_security/ccv/v1/ccv.proto @@ -56,7 +56,7 @@ message SlashPacketData { // unbonding operations. message MaturedUnbondingOps { repeated uint64 ids = 1; } -// ConsumerPacketData contains a consumer packet data and a type tag +// ConsumerPacketData contains a consumer packet data, type tag, and index for storage. message ConsumerPacketData { ConsumerPacketDataType type = 1; @@ -64,9 +64,12 @@ message ConsumerPacketData { SlashPacketData slashPacketData = 2; VSCMaturedPacketData vscMaturedPacketData = 3; } + uint64 idx = 4; } + // ConsumerPacketDataList is a list of consumer packet data packets. +// NOTE: It is only used for exporting / importing state in InitGenesis and ExportGenesis. message ConsumerPacketDataList { repeated ConsumerPacketData list = 1 [ (gogoproto.nullable) = false ]; } diff --git a/tests/integration/expired_client.go b/tests/integration/expired_client.go index 38d2a3ab5e..53863d2881 100644 --- a/tests/integration/expired_client.go +++ b/tests/integration/expired_client.go @@ -125,7 +125,7 @@ func (s *CCVTestSuite) TestConsumerPacketSendExpiredClient() { // check that the packets were added to the list of pending data packets consumerPackets := consumerKeeper.GetPendingPackets(s.consumerCtx()) s.Require().NotEmpty(consumerPackets) - s.Require().Equal(2, len(consumerPackets.GetList()), "unexpected number of pending data packets") + s.Require().Len(consumerPackets, 2, "unexpected number of pending data packets") // try to send slash packet for downtime infraction addr := ed25519.GenPrivKey().PubKey().Address() @@ -139,7 +139,7 @@ func (s *CCVTestSuite) TestConsumerPacketSendExpiredClient() { // check that the packets were added to the list of pending data packets consumerPackets = consumerKeeper.GetPendingPackets(s.consumerCtx()) s.Require().NotEmpty(consumerPackets) - s.Require().Equal(4, len(consumerPackets.GetList()), "unexpected number of pending data packets") + s.Require().Len(consumerPackets, 4, "unexpected number of pending data packets") // upgrade expired client to the consumer upgradeExpiredClient(s, Provider) @@ -150,7 +150,6 @@ func (s *CCVTestSuite) TestConsumerPacketSendExpiredClient() { // check that the list of pending data packets is emptied consumerPackets = consumerKeeper.GetPendingPackets(s.consumerCtx()) s.Require().Empty(consumerPackets) - s.Require().Equal(0, len(consumerPackets.GetList()), "unexpected number of pending data packets") // relay all packet from consumer to provider relayAllCommittedPackets(s, s.consumerChain, s.path, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, 4) diff --git a/tests/integration/slashing.go b/tests/integration/slashing.go index 28ae9da958..2bc960fd03 100644 --- a/tests/integration/slashing.go +++ b/tests/integration/slashing.go @@ -486,15 +486,15 @@ func (suite *CCVTestSuite) TestValidatorDowntime() { // check that slash packet is queued pendingPackets := consumerKeeper.GetPendingPackets(ctx) - suite.Require().NotEmpty(pendingPackets.List, "pending packets empty") - suite.Require().Len(pendingPackets.List, 1, "pending packets len should be 1 is %d", len(pendingPackets.List)) + suite.Require().NotEmpty(pendingPackets, "pending packets empty") + suite.Require().Len(pendingPackets, 1, "pending packets len should be 1 is %d", len(pendingPackets)) // clear queue, commit packets suite.consumerApp.GetConsumerKeeper().SendPackets(ctx) // check queue was cleared pendingPackets = suite.consumerApp.GetConsumerKeeper().GetPendingPackets(ctx) - suite.Require().Empty(pendingPackets.List, "pending packets NOT empty") + suite.Require().Empty(pendingPackets, "pending packets NOT empty") // verify that the slash packet was sent gotCommit := consumerIBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, ccv.ConsumerPortID, channelID, seq) @@ -573,15 +573,15 @@ func (suite *CCVTestSuite) TestValidatorDoubleSigning() { // check slash packet is queued pendingPackets := suite.consumerApp.GetConsumerKeeper().GetPendingPackets(ctx) - suite.Require().NotEmpty(pendingPackets.List, "pending packets empty") - suite.Require().Len(pendingPackets.List, 1, "pending packets len should be 1 is %d", len(pendingPackets.List)) + suite.Require().NotEmpty(pendingPackets, "pending packets empty") + suite.Require().Len(pendingPackets, 1, "pending packets len should be 1 is %d", len(pendingPackets)) // clear queue, commit packets suite.consumerApp.GetConsumerKeeper().SendPackets(ctx) // check queue was cleared pendingPackets = suite.consumerApp.GetConsumerKeeper().GetPendingPackets(ctx) - suite.Require().Empty(pendingPackets.List, "pending packets NOT empty") + suite.Require().Empty(pendingPackets, "pending packets NOT empty") // check slash packet is sent gotCommit := suite.consumerApp.GetIBCKeeper().ChannelKeeper.GetPacketCommitment(ctx, ccv.ConsumerPortID, channelID, seq) @@ -636,7 +636,7 @@ func (suite *CCVTestSuite) TestQueueAndSendSlashPacket() { // the downtime slash request duplicates dataPackets := consumerKeeper.GetPendingPackets(ctx) suite.Require().NotEmpty(dataPackets) - suite.Require().Len(dataPackets.GetList(), 12) + suite.Require().Len(dataPackets, 12) // save consumer next sequence seq, _ := consumerIBCKeeper.ChannelKeeper.GetNextSequenceSend(ctx, ccv.ConsumerPortID, channelID) @@ -663,7 +663,7 @@ func (suite *CCVTestSuite) TestQueueAndSendSlashPacket() { // check that pending data packets got cleared dataPackets = consumerKeeper.GetPendingPackets(ctx) suite.Require().Empty(dataPackets) - suite.Require().Len(dataPackets.GetList(), 0) + suite.Require().Len(dataPackets, 0) } // TestCISBeforeCCVEstablished tests that the consumer chain doesn't panic or @@ -674,14 +674,14 @@ func (suite *CCVTestSuite) TestCISBeforeCCVEstablished() { // Check pending packets is empty pendingPackets := consumerKeeper.GetPendingPackets(suite.consumerCtx()) - suite.Require().Len(pendingPackets.List, 0) + suite.Require().Len(pendingPackets, 0) consumerKeeper.SlashWithInfractionReason(suite.consumerCtx(), []byte{0x01, 0x02, 0x3}, 66, 4324, sdk.MustNewDecFromStr("0.05"), stakingtypes.Infraction_INFRACTION_DOWNTIME) // Check slash packet was queued pendingPackets = consumerKeeper.GetPendingPackets(suite.consumerCtx()) - suite.Require().Len(pendingPackets.List, 1) + suite.Require().Len(pendingPackets, 1) // Pass 5 blocks, confirming the consumer doesn't panic for i := 0; i < 5; i++ { @@ -690,7 +690,7 @@ func (suite *CCVTestSuite) TestCISBeforeCCVEstablished() { // Check packet is still queued pendingPackets = consumerKeeper.GetPendingPackets(suite.consumerCtx()) - suite.Require().Len(pendingPackets.List, 1) + suite.Require().Len(pendingPackets, 1) // establish ccv channel suite.SetupCCVChannel(suite.path) @@ -699,5 +699,5 @@ func (suite *CCVTestSuite) TestCISBeforeCCVEstablished() { // Pass one more block, and confirm the packet is sent now that ccv channel is established suite.consumerChain.NextBlock() pendingPackets = consumerKeeper.GetPendingPackets(suite.consumerCtx()) - suite.Require().Len(pendingPackets.List, 0) + suite.Require().Len(pendingPackets, 0) } diff --git a/x/ccv/consumer/keeper/genesis.go b/x/ccv/consumer/keeper/genesis.go index 45ea54f49c..a55184fd27 100644 --- a/x/ccv/consumer/keeper/genesis.go +++ b/x/ccv/consumer/keeper/genesis.go @@ -90,9 +90,12 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state *consumertypes.GenesisState) k.SetLastTransmissionBlockHeight(ctx, state.LastTransmissionBlockHeight) } - // set pending consumer pending packets + // Set pending consumer packets, using the depreciated ConsumerPacketDataList type + // that exists for genesis. // note that the list includes pending mature VSC packet only if the handshake is completed - k.AppendPendingPacket(ctx, state.PendingConsumerPackets.List...) + for _, packet := range state.PendingConsumerPackets.List { + k.AppendPendingPacket(ctx, packet.Type, packet.Data) + } // set height to valset update id mapping for _, h2v := range state.HeightToValsetUpdateId { @@ -122,6 +125,11 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisSt // export the current validator set valset := k.MustGetCurrentValidatorsAsABCIUpdates(ctx) + // export pending packets using the depreciated ConsumerPacketDataList type + pendingPackets := k.GetPendingPackets(ctx) + pendingPacketsDepreciated := ccv.ConsumerPacketDataList{} + pendingPacketsDepreciated.List = append(pendingPacketsDepreciated.List, pendingPackets...) + // export all the states created after a provider channel got established if channelID, ok := k.GetProviderChannel(ctx); ok { clientID, found := k.GetProviderClientID(ctx) @@ -136,7 +144,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisSt k.GetAllPacketMaturityTimes(ctx), valset, k.GetAllHeightToValsetUpdateIDs(ctx), - k.GetPendingPackets(ctx), + pendingPacketsDepreciated, k.GetAllOutstandingDowntimes(ctx), k.GetLastTransmissionBlockHeight(ctx), params, @@ -156,7 +164,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisSt nil, valset, k.GetAllHeightToValsetUpdateIDs(ctx), - k.GetPendingPackets(ctx), + pendingPacketsDepreciated, nil, consumertypes.LastTransmissionBlockHeight{}, params, diff --git a/x/ccv/consumer/keeper/genesis_test.go b/x/ccv/consumer/keeper/genesis_test.go index 47d834f8a8..5058f6ff5c 100644 --- a/x/ccv/consumer/keeper/genesis_test.go +++ b/x/ccv/consumer/keeper/genesis_test.go @@ -148,7 +148,12 @@ func TestInitGenesis(t *testing.T) { func(ctx sdk.Context, ck consumerkeeper.Keeper, gs *consumertypes.GenesisState) { assertConsumerPortIsBound(t, ctx, &ck) - require.Equal(t, pendingDataPackets, ck.GetPendingPackets(ctx)) + obtainedPendingPackets := ck.GetPendingPackets(ctx) + for idx, expectedPacketData := range pendingDataPackets.List { + require.Equal(t, expectedPacketData.Type, obtainedPendingPackets[idx].Type) + require.Equal(t, expectedPacketData.Data, obtainedPendingPackets[idx].Data) + } + assertHeightValsetUpdateIDs(t, ctx, &ck, defaultHeightValsetUpdateIDs) assertProviderClientID(t, ctx, &ck, provClientID) require.Equal(t, validator.Address.Bytes(), ck.GetAllCCValidator(ctx)[0].Address) @@ -186,7 +191,12 @@ func TestInitGenesis(t *testing.T) { require.Equal(t, provChannelID, gotChannelID) require.True(t, ck.PacketMaturityTimeExists(ctx, matPackets[0].VscId, matPackets[0].MaturityTime)) - require.Equal(t, pendingDataPackets, ck.GetPendingPackets(ctx)) + + obtainedPendingPackets := ck.GetPendingPackets(ctx) + for idx, expectedPacketData := range pendingDataPackets.List { + require.Equal(t, expectedPacketData.Type, obtainedPendingPackets[idx].Type) + require.Equal(t, expectedPacketData.Data, obtainedPendingPackets[idx].Data) + } require.Equal(t, gs.OutstandingDowntimeSlashing, ck.GetAllOutstandingDowntimes(ctx)) @@ -252,12 +262,16 @@ func TestExportGenesis(t *testing.T) { Data: &ccv.ConsumerPacketData_SlashPacketData{ SlashPacketData: ccv.NewSlashPacketData(abciValidator, vscID, stakingtypes.Infraction_INFRACTION_DOWNTIME), }, + Idx: 0, }, { Type: ccv.VscMaturedPacket, Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: ccv.NewVSCMaturedPacketData(vscID), }, + // This idx is a part of the expected genesis state. + // If the keeper is correctly storing consumer packet data, indexes should be populated. + Idx: 1, }, }, } @@ -291,7 +305,10 @@ func TestExportGenesis(t *testing.T) { ck.SetCCValidator(ctx, cVal) ck.SetParams(ctx, params) - ck.AppendPendingPacket(ctx, consPackets.List...) + for _, packet := range consPackets.List { + ck.AppendPendingPacket(ctx, packet.Type, packet.Data) + } + ck.SetHeightValsetUpdateID(ctx, defaultHeightValsetUpdateIDs[0].Height, defaultHeightValsetUpdateIDs[0].ValsetUpdateId) }, consumertypes.NewRestartGenesisState( @@ -321,7 +338,9 @@ func TestExportGenesis(t *testing.T) { ck.SetHeightValsetUpdateID(ctx, updatedHeightValsetUpdateIDs[0].Height, updatedHeightValsetUpdateIDs[0].ValsetUpdateId) ck.SetHeightValsetUpdateID(ctx, updatedHeightValsetUpdateIDs[1].Height, updatedHeightValsetUpdateIDs[1].ValsetUpdateId) - ck.AppendPendingPacket(ctx, consPackets.List...) + for _, packet := range consPackets.List { + ck.AppendPendingPacket(ctx, packet.Type, packet.Data) + } // populate the required states for an established CCV channel ck.SetPacketMaturityTime(ctx, matPackets[0].VscId, matPackets[0].MaturityTime) diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index 7da13ab433..cfff31b367 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -593,48 +593,78 @@ func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []stakingtypes.Val return validators } -// SetPendingPackets sets the pending CCV packets -func (k Keeper) SetPendingPackets(ctx sdk.Context, packets ccv.ConsumerPacketDataList) { +// getAndIncrementPendingPacketsIdx returns the current pending packets index and increments it. +// This index is used for implementing a FIFO queue of pending packets in the KV store. +func (k Keeper) getAndIncrementPendingPacketsIdx(ctx sdk.Context) (toReturn uint64) { store := ctx.KVStore(k.storeKey) - bz, err := packets.Marshal() - if err != nil { - // This should never happen - panic(fmt.Errorf("failed to marshal ConsumerPacketDataList: %w", err)) + bz := store.Get(types.PendingPacketsIndexKey()) + if bz != nil { + toReturn = sdk.BigEndianToUint64(bz) } - store.Set(types.PendingDataPacketsKey(), bz) + toStore := toReturn + 1 + store.Set(types.PendingPacketsIndexKey(), sdk.Uint64ToBigEndian(toStore)) + return toReturn } -// GetPendingPackets returns the pending CCV packets from the store -func (k Keeper) GetPendingPackets(ctx sdk.Context) ccv.ConsumerPacketDataList { - var packets ccv.ConsumerPacketDataList - +// GetPendingPackets returns ALL the pending CCV packets from the store +func (k Keeper) GetPendingPackets(ctx sdk.Context) []ccv.ConsumerPacketData { + var packets []ccv.ConsumerPacketData store := ctx.KVStore(k.storeKey) - bz := store.Get(types.PendingDataPacketsKey()) - if bz == nil { - return packets + // Note: PendingDataPacketsBytePrefix is the correct prefix, NOT PendingDataPacketsByteKey. + // See consistency with PendingDataPacketsKey(). + iterator := sdk.KVStorePrefixIterator(store, []byte{types.PendingDataPacketsBytePrefix}) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var packet ccv.ConsumerPacketData + bz := iterator.Value() + err := packet.Unmarshal(bz) + if err != nil { + // An error here would indicate something is very wrong, + panic(fmt.Errorf("failed to unmarshal pending data packet: %w", err)) + } + packets = append(packets, packet) } + return packets +} - err := packets.Unmarshal(bz) - if err != nil { - // An error here would indicate something is very wrong, - // the PendingPackets are assumed to be correctly serialized in SetPendingPackets. - panic(fmt.Errorf("failed to unmarshal pending data packets: %w", err)) +// DeletePendingDataPackets deletes pending data packets with given indexes +func (k Keeper) DeletePendingDataPackets(ctx sdk.Context, idxs ...uint64) { + store := ctx.KVStore(k.storeKey) + for _, idx := range idxs { + store.Delete(types.PendingDataPacketsKey(idx)) } - - return packets } -// DeletePendingDataPackets clears the pending data packets in store -func (k Keeper) DeletePendingDataPackets(ctx sdk.Context) { +func (k Keeper) DeleteAllPendingDataPackets(ctx sdk.Context) { store := ctx.KVStore(k.storeKey) - store.Delete(types.PendingDataPacketsKey()) + // Note: PendingDataPacketsBytePrefix is the correct prefix, NOT PendingDataPacketsByteKey. + // See consistency with PendingDataPacketsKey(). + iterator := sdk.KVStorePrefixIterator(store, []byte{types.PendingDataPacketsBytePrefix}) + keysToDel := [][]byte{} + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + for _, key := range keysToDel { + store.Delete(key) + } } -// AppendPendingDataPacket appends the given data packet to the pending data packets in store -func (k Keeper) AppendPendingPacket(ctx sdk.Context, packet ...ccv.ConsumerPacketData) { - pending := k.GetPendingPackets(ctx) - list := append(pending.GetList(), packet...) - k.SetPendingPackets(ctx, ccv.ConsumerPacketDataList{List: list}) +// AppendPendingPacket enqueues the given data packet to the end of the pending data packets queue +func (k Keeper) AppendPendingPacket(ctx sdk.Context, packetType ccv.ConsumerPacketDataType, data ccv.ExportedIsConsumerPacketData_Data) { + cpd := ccv.NewConsumerPacketData( + packetType, + data, + k.getAndIncrementPendingPacketsIdx(ctx), + ) + key := types.PendingDataPacketsKey(cpd.Idx) + store := ctx.KVStore(k.storeKey) + bz, err := cpd.Marshal() + if err != nil { + // This should never happen + panic(fmt.Errorf("failed to marshal ConsumerPacketData: %w", err)) + } + store.Set(key, bz) } func (k Keeper) MarkAsPrevStandaloneChain(ctx sdk.Context) { diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index 662a776cef..5802c9d590 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -314,23 +314,25 @@ func TestGetAllCCValidator(t *testing.T) { require.Equal(t, result, expectedGetAllOrder) } -func TestSetPendingPackets(t *testing.T) { +func TestPendingPackets(t *testing.T) { consumerKeeper, ctx, ctrl, _ := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - // prepare test setup - dataPackets := []ccv.ConsumerPacketData{ + // Instantiate some expected packet data + packetData := []ccv.ConsumerPacketData{ { Type: ccv.VscMaturedPacket, Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: ccv.NewVSCMaturedPacketData(1), }, + Idx: 0, // Note these are expected idxs, we don't pass this data to the keeper }, { Type: ccv.VscMaturedPacket, Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: ccv.NewVSCMaturedPacketData(2), }, + Idx: 1, }, { Type: ccv.SlashPacket, @@ -341,19 +343,24 @@ func TestSetPendingPackets(t *testing.T) { stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN, ), }, + Idx: 2, }, { Type: ccv.VscMaturedPacket, Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: ccv.NewVSCMaturedPacketData(3), }, + Idx: 3, }, } - consumerKeeper.SetPendingPackets(ctx, ccv.ConsumerPacketDataList{List: dataPackets}) - storedDataPackets := consumerKeeper.GetPendingPackets(ctx) - require.NotEmpty(t, storedDataPackets) - require.Equal(t, dataPackets, storedDataPackets.List) + // Append all packets to the queue + for _, data := range packetData { + consumerKeeper.AppendPendingPacket(ctx, data.Type, data.Data) + } + storedPacketData := consumerKeeper.GetPendingPackets(ctx) + require.NotEmpty(t, storedPacketData) + require.Equal(t, packetData, storedPacketData) slashPacket := ccv.NewSlashPacketData( abci.Validator{ @@ -363,31 +370,50 @@ func TestSetPendingPackets(t *testing.T) { uint64(4), stakingtypes.Infraction_INFRACTION_DOWNTIME, ) - dataPackets = append(dataPackets, ccv.ConsumerPacketData{ + // Append slash packet to expected packet data + packetData = append(packetData, ccv.ConsumerPacketData{ Type: ccv.SlashPacket, - Data: &ccv.ConsumerPacketData_SlashPacketData{SlashPacketData: slashPacket}, - }, - ) - consumerKeeper.AppendPendingPacket(ctx, dataPackets[len(dataPackets)-1]) - storedDataPackets = consumerKeeper.GetPendingPackets(ctx) - require.NotEmpty(t, storedDataPackets) - require.Equal(t, dataPackets, storedDataPackets.List) + Data: &ccv.ConsumerPacketData_SlashPacketData{ + SlashPacketData: slashPacket, + }, + Idx: 4, + }) - vscMaturedPakcet := ccv.NewVSCMaturedPacketData(4) - dataPackets = append(dataPackets, ccv.ConsumerPacketData{ + toAppend := packetData[len(packetData)-1] + consumerKeeper.AppendPendingPacket(ctx, toAppend.Type, toAppend.Data) + storedPacketData = consumerKeeper.GetPendingPackets(ctx) + require.NotEmpty(t, storedPacketData) + require.Equal(t, packetData, storedPacketData) + + vscMaturedPacket := ccv.NewVSCMaturedPacketData(4) + packetData = append(packetData, ccv.ConsumerPacketData{ Type: ccv.VscMaturedPacket, - Data: &ccv.ConsumerPacketData_VscMaturedPacketData{VscMaturedPacketData: vscMaturedPakcet}, - }, - ) - consumerKeeper.AppendPendingPacket(ctx, dataPackets[len(dataPackets)-1]) - storedDataPackets = consumerKeeper.GetPendingPackets(ctx) - require.NotEmpty(t, storedDataPackets) - require.Equal(t, dataPackets, storedDataPackets.List) - - consumerKeeper.DeletePendingDataPackets(ctx) - storedDataPackets = consumerKeeper.GetPendingPackets(ctx) - require.Empty(t, storedDataPackets) - require.Len(t, storedDataPackets.List, 0) + Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: vscMaturedPacket, + }, + Idx: 5, + }) + toAppend = packetData[len(packetData)-1] + consumerKeeper.AppendPendingPacket(ctx, toAppend.Type, toAppend.Data) + + storedPacketData = consumerKeeper.GetPendingPackets(ctx) + require.NotEmpty(t, storedPacketData) + require.Equal(t, packetData, storedPacketData) + + // Delete packet with idx 5 (final index) + consumerKeeper.DeletePendingDataPackets(ctx, 5) + storedPacketData = consumerKeeper.GetPendingPackets(ctx) + require.Equal(t, packetData[:len(packetData)-1], storedPacketData) + + // Delete packet with idx 0 (first index) + consumerKeeper.DeletePendingDataPackets(ctx, 0) + storedPacketData = consumerKeeper.GetPendingPackets(ctx) + require.Equal(t, packetData[1:len(packetData)-1], storedPacketData) + + // Delete all packets + consumerKeeper.DeleteAllPendingDataPackets(ctx) + storedPacketData = consumerKeeper.GetPendingPackets(ctx) + require.Empty(t, storedPacketData) } // TestVerifyProviderChain tests the VerifyProviderChain method for the consumer keeper diff --git a/x/ccv/consumer/keeper/migration.go b/x/ccv/consumer/keeper/migration.go new file mode 100644 index 0000000000..361bb2a62f --- /dev/null +++ b/x/ccv/consumer/keeper/migration.go @@ -0,0 +1,70 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + + consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + ccvConsumerKeeper Keeper + ccvConsumerParamSpace paramtypes.Subspace +} + +// NewMigrator returns a new Migrator. +func NewMigrator(ccvConsumerKeeper Keeper, ccvConsumerParamSpace paramtypes.Subspace) Migrator { + return Migrator{ccvConsumerKeeper: ccvConsumerKeeper, ccvConsumerParamSpace: ccvConsumerParamSpace} +} + +// MigrateConsumerPacketData migrates consumer packet data according to +// https://github.com/cosmos/interchain-security/pull/1037 +// +// Note an equivalent migration is not required for providers. +func (k Keeper) MigrateConsumerPacketData(ctx sdk.Context) { + // deserialize packet data from old format + var depreciatedType ccvtypes.ConsumerPacketDataList + store := ctx.KVStore(k.storeKey) + bz := store.Get([]byte{consumertypes.PendingDataPacketsBytePrefix}) + if bz == nil { + ctx.Logger().Info("no pending data packets to migrate") + return + } + err := depreciatedType.Unmarshal(bz) + if err != nil { + // An error here would indicate something is very wrong + panic(fmt.Errorf("failed to unmarshal pending data packets: %w", err)) + } + + // Delete old data + store.Delete([]byte{consumertypes.PendingDataPacketsBytePrefix}) + + // re-serialize packet data in new format, with the same key prefix, + // where indexes are added internally to AppendPendingPacket. + for _, data := range depreciatedType.List { + k.AppendPendingPacket(ctx, data.Type, data.Data) + } +} + +// TODO: the following hackyness could be removed if we're able to reference older versions of ICS. +// This would likely require go.mod split, and a testing module that could depend on multiple ICS versions. + +func PendingDataPacketsKeyOnlyForTesting() []byte { + return []byte{consumertypes.PendingDataPacketsBytePrefix} // Assumes keys haven't been shuffled +} + +// Note: a better test of the old functionality would be to directly reference the old ICS version, +// including the version of ccv.ConsumerPacketDataList has a list of ccv.ConsumerPacketData without indexes. +func (k Keeper) SetPendingPacketsOnlyForTesting(ctx sdk.Context, packets ccvtypes.ConsumerPacketDataList) { + store := ctx.KVStore(k.storeKey) + bz, err := packets.Marshal() + if err != nil { + // This should never happen + panic(fmt.Errorf("failed to marshal ConsumerPacketDataList: %w", err)) + } + store.Set(PendingDataPacketsKeyOnlyForTesting(), bz) +} diff --git a/x/ccv/consumer/keeper/migration_test.go b/x/ccv/consumer/keeper/migration_test.go new file mode 100644 index 0000000000..1e7bc54bdf --- /dev/null +++ b/x/ccv/consumer/keeper/migration_test.go @@ -0,0 +1,68 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + testutil "github.com/cosmos/interchain-security/v3/testutil/keeper" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" +) + +func TestMigrateConsumerPacketData(t *testing.T) { + consumerKeeper, ctx, ctrl, _ := testutil.GetConsumerKeeperAndCtx(t, testutil.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // Set some pending data packets in the old format + packets := ccvtypes.ConsumerPacketDataList{ + List: []ccvtypes.ConsumerPacketData{ + { + Type: ccvtypes.SlashPacket, + Data: &ccvtypes.ConsumerPacketData_SlashPacketData{ + SlashPacketData: &ccvtypes.SlashPacketData{ + ValsetUpdateId: 77, + }, + }, + }, + { + Type: ccvtypes.VscMaturedPacket, + Data: &ccvtypes.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: &ccvtypes.VSCMaturedPacketData{ + ValsetUpdateId: 88, + }, + }, + }, + { + Type: ccvtypes.VscMaturedPacket, + Data: &ccvtypes.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: &ccvtypes.VSCMaturedPacketData{ + ValsetUpdateId: 99, + }, + }, + }, + }, + } + + // Set old data + consumerKeeper.SetPendingPacketsOnlyForTesting(ctx, packets) + + // Migrate + consumerKeeper.MigrateConsumerPacketData(ctx) + + // Check that the data is migrated properly + obtainedPackets := consumerKeeper.GetPendingPackets(ctx) + require.Len(t, packets.List, 3) + + require.Equal(t, ccvtypes.SlashPacket, obtainedPackets[0].Type) + require.Equal(t, ccvtypes.VscMaturedPacket, obtainedPackets[1].Type) + require.Equal(t, ccvtypes.VscMaturedPacket, obtainedPackets[2].Type) + + require.Equal(t, uint64(77), obtainedPackets[0].GetSlashPacketData().ValsetUpdateId) + require.Equal(t, uint64(88), obtainedPackets[1].GetVscMaturedPacketData().ValsetUpdateId) + require.Equal(t, uint64(99), obtainedPackets[2].GetVscMaturedPacketData().ValsetUpdateId) + + // Check that indexes are populated + require.Equal(t, uint64(0), obtainedPackets[0].Idx) + require.Equal(t, uint64(1), obtainedPackets[1].Idx) + require.Equal(t, uint64(2), obtainedPackets[2].Idx) +} diff --git a/x/ccv/consumer/keeper/relay.go b/x/ccv/consumer/keeper/relay.go index cd5105421e..073a1d3996 100644 --- a/x/ccv/consumer/keeper/relay.go +++ b/x/ccv/consumer/keeper/relay.go @@ -104,10 +104,10 @@ func (k Keeper) QueueVSCMaturedPackets(ctx sdk.Context) { // Append VSCMatured packet to pending packets. // Sending packets is attempted each EndBlock. // Unsent packets remain in the queue until sent. - k.AppendPendingPacket(ctx, ccv.ConsumerPacketData{ - Type: ccv.VscMaturedPacket, - Data: &ccv.ConsumerPacketData_VscMaturedPacketData{VscMaturedPacketData: vscPacket}, - }) + k.AppendPendingPacket(ctx, + ccv.VscMaturedPacket, + &ccv.ConsumerPacketData_VscMaturedPacketData{VscMaturedPacketData: vscPacket}, + ) k.DeletePacketMaturityTimes(ctx, maturityTime.VscId, maturityTime.MaturityTime) @@ -147,12 +147,12 @@ func (k Keeper) QueueSlashPacket(ctx sdk.Context, validator abci.Validator, vals // append the Slash packet data to pending data packets // to be sent once the CCV channel is established - k.AppendPendingPacket(ctx, ccv.ConsumerPacketData{ - Type: ccv.SlashPacket, - Data: &ccv.ConsumerPacketData_SlashPacketData{ + k.AppendPendingPacket(ctx, + ccv.SlashPacket, + &ccv.ConsumerPacketData_SlashPacketData{ SlashPacketData: slashPacket, }, - }) + ) k.Logger(ctx).Info("SlashPacket enqueued", "vscID", slashPacket.ValsetUpdateId, @@ -186,7 +186,8 @@ func (k Keeper) SendPackets(ctx sdk.Context) { } pending := k.GetPendingPackets(ctx) - for _, p := range pending.GetList() { + for _, p := range pending { + // send packet over IBC err := ccv.SendIBCPacket( ctx, @@ -213,8 +214,7 @@ func (k Keeper) SendPackets(ctx sdk.Context) { } } - // clear pending data packets - k.DeletePendingDataPackets(ctx) + k.DeleteAllPendingDataPackets(ctx) } // OnAcknowledgementPacket executes application logic for acknowledgments of sent VSCMatured and Slash packets diff --git a/x/ccv/consumer/keeper/relay_test.go b/x/ccv/consumer/keeper/relay_test.go index 962fc943bd..48ad7f5ab9 100644 --- a/x/ccv/consumer/keeper/relay_test.go +++ b/x/ccv/consumer/keeper/relay_test.go @@ -293,9 +293,9 @@ func TestSendPacketsFailure(t *testing.T) { consumerKeeper.SetParams(ctx, consumertypes.DefaultParams()) // Set some pending packets - consumerKeeper.SetPendingPackets(ctx, types.ConsumerPacketDataList{List: []types.ConsumerPacketData{ - {}, {}, {}, - }}) + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{}) + consumerKeeper.AppendPendingPacket(ctx, types.SlashPacket, &types.ConsumerPacketData_SlashPacketData{}) + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{}) // Mock the channel keeper to return an error gomock.InOrder( @@ -305,5 +305,5 @@ func TestSendPacketsFailure(t *testing.T) { // No panic should occur, pending packets should not be cleared consumerKeeper.SendPackets(ctx) - require.Equal(t, 3, len(consumerKeeper.GetPendingPackets(ctx).List)) + require.Equal(t, 3, len(consumerKeeper.GetPendingPackets(ctx))) } diff --git a/x/ccv/consumer/keeper/validators_test.go b/x/ccv/consumer/keeper/validators_test.go index 67ab91f531..dda1ebab19 100644 --- a/x/ccv/consumer/keeper/validators_test.go +++ b/x/ccv/consumer/keeper/validators_test.go @@ -154,7 +154,7 @@ func TestSlash(t *testing.T) { // If we call slash with infraction type empty, no slash packet will be queued consumerKeeper.SlashWithInfractionReason(ctx, []byte{0x01, 0x02, 0x03}, 5, 6, sdk.NewDec(9.0), stakingtypes.Infraction_INFRACTION_UNSPECIFIED) pendingPackets := consumerKeeper.GetPendingPackets(ctx) - require.Len(t, pendingPackets.List, 0) + require.Len(t, pendingPackets, 0) // Consumer keeper from test setup should return false for IsPrevStandaloneChain() require.False(t, consumerKeeper.IsPrevStandaloneChain(ctx)) @@ -165,7 +165,7 @@ func TestSlash(t *testing.T) { // Call slash with valid infraction type and confirm 1 slash packet is queued consumerKeeper.SlashWithInfractionReason(ctx, []byte{0x01, 0x02, 0x03}, 5, 6, sdk.NewDec(9.0), stakingtypes.Infraction_INFRACTION_DOWNTIME) pendingPackets = consumerKeeper.GetPendingPackets(ctx) - require.Len(t, pendingPackets.List, 1) + require.Len(t, pendingPackets, 1) // Next, we set a value for the standalone staking keeper, // and mark the consumer keeper as being from a previous standalone chain diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index 0ceed2814e..8b792419ef 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -98,6 +98,10 @@ const ( // PrevStandaloneChainByteKey is the byte storing the flag marking whether this chain was previously standalone PrevStandaloneChainByteKey + // PendingPacketsIndexBytePrefix is the single byte key to the pending packets index. + // This index is used for implementing a FIFO queue of pending packets in the KV store. + PendingPacketsIndexByteKey + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go ) @@ -172,11 +176,13 @@ func CrossChainValidatorKey(addr []byte) []byte { return append([]byte{CrossChainValidatorBytePrefix}, addr...) } -// PendingDataPacketsKey returns the key for storing a list of data packets -// that cannot be sent yet to the provider chain either because the CCV channel -// is not established or because the client is expired. -func PendingDataPacketsKey() []byte { - return []byte{PendingDataPacketsBytePrefix} +// PendingDataPacketsKey returns the key for storing a queue of data packets to be sent to the provider. +// Packets in this queue will not be sent on the next endblocker if: +// - the CCV channel is not yet established +// - the client is expired +// - A slash packet is being bounced between consumer and provider (not yet implemented) +func PendingDataPacketsKey(idx uint64) []byte { + return append([]byte{PendingDataPacketsBytePrefix}, sdk.Uint64ToBigEndian(idx)...) } func PreCCVKey() []byte { @@ -206,6 +212,12 @@ func PrevStandaloneChainKey() []byte { return []byte{PrevStandaloneChainByteKey} } +// PendingPacketsIndexKey returns the key to the pending packets index. +// This index is used for implementing a FIFO queue of pending packets in the KV store. +func PendingPacketsIndexKey() []byte { + return []byte{PendingPacketsIndexByteKey} +} + // NOTE: DO NOT ADD FULLY DEFINED KEY FUNCTIONS WITHOUT ADDING THEM TO getAllFullyDefinedKeys() IN keys_test.go // diff --git a/x/ccv/consumer/types/keys_test.go b/x/ccv/consumer/types/keys_test.go index a63da6f326..5290dd3599 100644 --- a/x/ccv/consumer/types/keys_test.go +++ b/x/ccv/consumer/types/keys_test.go @@ -41,6 +41,7 @@ func getAllKeyPrefixes() []byte { InitGenesisHeightByteKey, StandaloneTransferChannelIDByteKey, PrevStandaloneChainByteKey, + PendingPacketsIndexByteKey, } } @@ -72,10 +73,11 @@ func getAllFullyDefinedKeys() [][]byte { PacketMaturityTimeKey(0, time.Time{}), HeightValsetUpdateIDKey(0), OutstandingDowntimeKey([]byte{}), - PendingDataPacketsKey(), + PendingDataPacketsKey(473289), CrossChainValidatorKey([]byte{}), InitGenesisHeightKey(), StandaloneTransferChannelIDKey(), PrevStandaloneChainKey(), + PendingPacketsIndexKey(), } } diff --git a/x/ccv/types/ccv.go b/x/ccv/types/ccv.go index f02ed1c450..453c55d252 100644 --- a/x/ccv/types/ccv.go +++ b/x/ccv/types/ccv.go @@ -164,3 +164,17 @@ func (vdt1 SlashPacketDataV1) FromV1() *SlashPacketData { Infraction: newType, } } + +// An exported wrapper around the auto generated isConsumerPacketData_Data interface, only for +// AppendPendingPacket to accept the interface as an argument. +type ExportedIsConsumerPacketData_Data interface { + isConsumerPacketData_Data +} + +func NewConsumerPacketData(cpdType ConsumerPacketDataType, data isConsumerPacketData_Data, idx uint64) ConsumerPacketData { + return ConsumerPacketData{ + Type: cpdType, + Data: data, + Idx: idx, + } +} diff --git a/x/ccv/types/ccv.pb.go b/x/ccv/types/ccv.pb.go index c2f0bd3ecc..c5eacc2288 100644 --- a/x/ccv/types/ccv.pb.go +++ b/x/ccv/types/ccv.pb.go @@ -361,13 +361,14 @@ func (m *MaturedUnbondingOps) GetIds() []uint64 { return nil } -// ConsumerPacketData contains a consumer packet data and a type tag +// ConsumerPacketData contains a consumer packet data, type tag, and index for storage. type ConsumerPacketData struct { Type ConsumerPacketDataType `protobuf:"varint,1,opt,name=type,proto3,enum=interchain_security.ccv.v1.ConsumerPacketDataType" json:"type,omitempty"` // Types that are valid to be assigned to Data: // *ConsumerPacketData_SlashPacketData // *ConsumerPacketData_VscMaturedPacketData Data isConsumerPacketData_Data `protobuf_oneof:"data"` + Idx uint64 `protobuf:"varint,4,opt,name=idx,proto3" json:"idx,omitempty"` } func (m *ConsumerPacketData) Reset() { *m = ConsumerPacketData{} } @@ -447,6 +448,13 @@ func (m *ConsumerPacketData) GetVscMaturedPacketData() *VSCMaturedPacketData { return nil } +func (m *ConsumerPacketData) GetIdx() uint64 { + if m != nil { + return m.Idx + } + return 0 +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*ConsumerPacketData) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -456,6 +464,7 @@ func (*ConsumerPacketData) XXX_OneofWrappers() []interface{} { } // ConsumerPacketDataList is a list of consumer packet data packets. +// NOTE: It is only used for exporting / importing state in InitGenesis and ExportGenesis. type ConsumerPacketDataList struct { List []ConsumerPacketData `protobuf:"bytes,1,rep,name=list,proto3" json:"list"` } @@ -678,60 +687,61 @@ func init() { } var fileDescriptor_68bd5f3242e6f29c = []byte{ - // 836 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xcf, 0x6e, 0xe2, 0x46, - 0x1c, 0xb6, 0x01, 0xad, 0x9a, 0xa1, 0x22, 0xce, 0x2c, 0xad, 0x58, 0x6f, 0xcb, 0x5a, 0xd6, 0x4a, + // 849 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0xcf, 0x6e, 0xe2, 0x46, + 0x1c, 0xc6, 0x80, 0x56, 0xcd, 0x50, 0x11, 0x67, 0x96, 0x56, 0x5e, 0x6f, 0xcb, 0x5a, 0xd6, 0x4a, 0x45, 0xa9, 0xd6, 0x2e, 0x64, 0x0f, 0x55, 0x7b, 0x69, 0x00, 0xa7, 0x71, 0x9b, 0x90, 0xc8, 0x06, - 0x56, 0xdb, 0x8b, 0x35, 0xd8, 0x13, 0x32, 0x0a, 0xd8, 0xc8, 0x33, 0xb8, 0xe5, 0x0d, 0x2a, 0x4e, - 0x7d, 0x01, 0x4e, 0x55, 0x0f, 0xfb, 0x18, 0xbd, 0xed, 0x71, 0xa5, 0x5e, 0xf6, 0xd2, 0xa8, 0x4a, - 0xde, 0xa0, 0x4f, 0x50, 0xd9, 0xfc, 0xc7, 0x06, 0x29, 0x52, 0xa5, 0xf6, 0x84, 0x19, 0xff, 0xbe, - 0x4f, 0xf3, 0xfd, 0x19, 0x79, 0xc0, 0x73, 0xe2, 0x32, 0xec, 0xdb, 0xd7, 0x88, 0xb8, 0x16, 0xc5, - 0xf6, 0xd0, 0x27, 0x6c, 0xa4, 0xda, 0x76, 0xa0, 0x06, 0xe5, 0xf0, 0x47, 0x19, 0xf8, 0x1e, 0xf3, - 0xa0, 0x98, 0x30, 0xa5, 0x84, 0xaf, 0x83, 0xb2, 0xf8, 0xdc, 0xf6, 0x68, 0xdf, 0xa3, 0x2a, 0x65, - 0xe8, 0x86, 0xb8, 0x5d, 0x35, 0x28, 0x77, 0x30, 0x43, 0xe5, 0xf9, 0xff, 0x29, 0x83, 0x98, 0xef, - 0x7a, 0x5d, 0x2f, 0x7a, 0x54, 0xc3, 0xa7, 0xd9, 0xea, 0x53, 0x86, 0x5d, 0x07, 0xfb, 0x7d, 0xe2, - 0x32, 0x15, 0x75, 0x6c, 0xa2, 0xb2, 0xd1, 0x00, 0xd3, 0xe9, 0x4b, 0xf9, 0x3d, 0x0f, 0x3e, 0x69, - 0xa3, 0x1e, 0x71, 0x10, 0xf3, 0x7c, 0x13, 0xb3, 0xda, 0x35, 0x72, 0xbb, 0xf8, 0x12, 0xd9, 0x37, - 0x98, 0xd5, 0x11, 0x43, 0xd0, 0x03, 0x07, 0xc1, 0xfc, 0xbd, 0x35, 0x1c, 0x38, 0x88, 0x61, 0x5a, - 0xe0, 0xa5, 0x74, 0x29, 0x5b, 0x91, 0x94, 0x25, 0xb3, 0x12, 0x32, 0x2b, 0x0b, 0xa6, 0x56, 0x34, - 0x58, 0x95, 0xde, 0xde, 0x3e, 0xe3, 0xfe, 0xbe, 0x7d, 0x56, 0x18, 0xa1, 0x7e, 0xef, 0x2b, 0x39, - 0x46, 0x24, 0x1b, 0x42, 0xb0, 0x0e, 0xa1, 0xb0, 0x04, 0xc2, 0x35, 0x8a, 0xd9, 0x6c, 0xc8, 0x22, - 0x4e, 0x21, 0x25, 0xf1, 0xa5, 0x8c, 0x91, 0x9b, 0xae, 0x4f, 0x07, 0x75, 0x07, 0x7e, 0x0a, 0x00, - 0xed, 0x21, 0x7a, 0x6d, 0x21, 0xfb, 0x86, 0x16, 0xd2, 0x52, 0xba, 0xb4, 0x67, 0xec, 0x45, 0x2b, - 0xc7, 0xf6, 0x0d, 0x95, 0x3d, 0xf0, 0x64, 0x9b, 0x32, 0x0a, 0x0d, 0x90, 0xe9, 0x11, 0xca, 0x66, - 0x4a, 0xbe, 0x54, 0xb6, 0x7b, 0xaf, 0xec, 0xb2, 0xa7, 0x9a, 0x09, 0x15, 0x1a, 0x11, 0x97, 0xfc, - 0x0d, 0xc8, 0xb7, 0xcd, 0xda, 0x39, 0x62, 0x43, 0x1f, 0x3b, 0x2b, 0x16, 0x26, 0x29, 0xe2, 0x93, - 0x14, 0xc9, 0x7f, 0xf0, 0x60, 0xdf, 0x0c, 0x05, 0xac, 0xa0, 0x0d, 0xb0, 0xb7, 0xf0, 0x28, 0x82, - 0x65, 0x2b, 0xe2, 0x76, 0xe3, 0xab, 0x85, 0x99, 0xe5, 0xc2, 0x86, 0xe5, 0xb2, 0xb1, 0xa4, 0x79, - 0x80, 0xc7, 0x55, 0x00, 0x88, 0x7b, 0xe5, 0x23, 0x9b, 0x11, 0xcf, 0x2d, 0xa4, 0x25, 0xbe, 0x94, - 0xab, 0xc8, 0xca, 0xb4, 0x8d, 0xca, 0xbc, 0x7d, 0xb3, 0x36, 0x2a, 0xfa, 0x62, 0xd2, 0x58, 0x41, - 0xc9, 0x9f, 0x81, 0xc7, 0x33, 0x53, 0x5a, 0x6e, 0xc7, 0x73, 0x1d, 0xe2, 0x76, 0x2f, 0x06, 0x14, - 0x0a, 0x20, 0x4d, 0x9c, 0x69, 0x97, 0x32, 0x46, 0xf8, 0x28, 0xff, 0x96, 0x02, 0xb0, 0xe6, 0xb9, - 0x74, 0xd8, 0xc7, 0xfe, 0x8a, 0x03, 0x27, 0x20, 0x13, 0x56, 0x36, 0x12, 0x9f, 0xab, 0x54, 0x76, - 0x65, 0x15, 0x47, 0x37, 0x47, 0x03, 0x6c, 0x44, 0x78, 0xf8, 0x0a, 0xec, 0xd3, 0x75, 0x73, 0x23, - 0xd1, 0xd9, 0xca, 0xe7, 0xbb, 0x28, 0x37, 0xf2, 0x38, 0xe5, 0x8c, 0x4d, 0x16, 0x78, 0x05, 0xf2, - 0x01, 0xb5, 0x63, 0xc1, 0x47, 0x76, 0x65, 0x2b, 0x5f, 0xec, 0x2c, 0x57, 0x42, 0x61, 0x4e, 0x39, - 0x23, 0x91, 0xaf, 0xfa, 0x08, 0x64, 0x1c, 0xc4, 0x90, 0xdc, 0x01, 0x1f, 0xc7, 0x85, 0x9e, 0x11, - 0xca, 0xe0, 0xe9, 0x5a, 0xad, 0x95, 0x87, 0x59, 0xb5, 0x56, 0xe6, 0x37, 0x29, 0x90, 0x8f, 0x8f, - 0xb4, 0xcb, 0xff, 0x5a, 0x1a, 0xaf, 0xb7, 0xa5, 0xf1, 0xe2, 0x01, 0x69, 0xb4, 0xcb, 0xff, 0x87, - 0x3c, 0xfe, 0xe4, 0xc1, 0x41, 0x6c, 0x63, 0xff, 0xf1, 0xc1, 0xfd, 0x2e, 0xe1, 0xe0, 0x1e, 0xee, - 0x52, 0xbe, 0x3c, 0xbc, 0x51, 0x48, 0x2b, 0xe8, 0xc3, 0xdf, 0xf9, 0xa4, 0xc2, 0x85, 0x63, 0xf0, - 0x6b, 0x20, 0xd5, 0x2e, 0x1a, 0x66, 0xeb, 0x5c, 0x33, 0xac, 0xcb, 0xe3, 0xda, 0xf7, 0x5a, 0xd3, - 0x6a, 0xbe, 0xbe, 0xd4, 0xac, 0x56, 0xc3, 0xbc, 0xd4, 0x6a, 0xfa, 0x89, 0xae, 0xd5, 0x05, 0x4e, - 0xfc, 0x68, 0x3c, 0x91, 0x0e, 0x5a, 0x2e, 0x1d, 0x60, 0x9b, 0x5c, 0x91, 0xb9, 0x87, 0x50, 0x05, - 0x62, 0x22, 0xd8, 0x3c, 0x3b, 0x36, 0x4f, 0x05, 0x5e, 0xdc, 0x1f, 0x4f, 0xa4, 0xec, 0x8a, 0xb1, - 0xf0, 0x08, 0x3c, 0x49, 0x04, 0x84, 0xa9, 0x09, 0x29, 0x31, 0x3f, 0x9e, 0x48, 0x42, 0x7b, 0x23, - 0x29, 0x31, 0xf3, 0xf3, 0xaf, 0x45, 0xee, 0xf0, 0x0d, 0x0f, 0x72, 0xeb, 0x12, 0xe1, 0x4b, 0xf0, - 0x54, 0x6f, 0x9c, 0x18, 0xc7, 0xb5, 0xa6, 0x7e, 0xd1, 0x48, 0xda, 0xf6, 0xe3, 0xf1, 0x44, 0xda, - 0x5f, 0x82, 0xb4, 0xfe, 0x80, 0x8d, 0xa0, 0x1a, 0x47, 0xd5, 0x2f, 0x5a, 0xd5, 0x33, 0xcd, 0x32, - 0xf5, 0x6f, 0x1b, 0x02, 0x2f, 0xe6, 0xc6, 0x13, 0x09, 0xd4, 0xbd, 0x61, 0xa7, 0x87, 0x4d, 0xd2, - 0x75, 0xe1, 0x21, 0x28, 0xc4, 0x01, 0xaf, 0x1a, 0x4d, 0xfd, 0x5c, 0x13, 0x52, 0xe2, 0x87, 0xe3, - 0x89, 0xf4, 0x41, 0xdd, 0xfb, 0xd1, 0x65, 0xa4, 0x8f, 0xa7, 0x7b, 0xad, 0x36, 0xde, 0xde, 0x15, - 0xf9, 0x77, 0x77, 0x45, 0xfe, 0xaf, 0xbb, 0x22, 0xff, 0xcb, 0x7d, 0x91, 0x7b, 0x77, 0x5f, 0xe4, - 0xde, 0xdf, 0x17, 0xb9, 0x1f, 0x5e, 0x76, 0x09, 0xbb, 0x1e, 0x76, 0x14, 0xdb, 0xeb, 0xab, 0xb3, - 0x2b, 0xc1, 0x32, 0xd2, 0x17, 0x8b, 0xbb, 0x45, 0x70, 0xa4, 0xfe, 0x14, 0x5d, 0x30, 0xa2, 0x4f, - 0x7d, 0xe7, 0x51, 0xf4, 0xad, 0x3f, 0xfa, 0x27, 0x00, 0x00, 0xff, 0xff, 0x8f, 0xbf, 0xfc, 0xd2, - 0x88, 0x08, 0x00, 0x00, + 0x56, 0xdb, 0x8b, 0x35, 0xd8, 0x13, 0x32, 0x0a, 0xd8, 0xc8, 0x33, 0xb8, 0xcb, 0x1b, 0x54, 0x9c, + 0xfa, 0x02, 0x9c, 0x7a, 0xda, 0x27, 0xe8, 0xb9, 0xb7, 0x3d, 0xae, 0xd4, 0xcb, 0x5e, 0xba, 0xaa, + 0x92, 0x37, 0xe8, 0x13, 0x54, 0xb6, 0x09, 0x7f, 0x62, 0x83, 0x14, 0x69, 0xa5, 0x3d, 0x31, 0x8c, + 0x7f, 0xdf, 0x27, 0x7f, 0x7f, 0x46, 0x1e, 0xf0, 0x94, 0xb8, 0x0c, 0xfb, 0xf6, 0x25, 0x22, 0xae, + 0x45, 0xb1, 0x3d, 0xf6, 0x09, 0x9b, 0xa8, 0xb6, 0x1d, 0xa8, 0x41, 0x35, 0xfc, 0x51, 0x46, 0xbe, + 0xc7, 0x3c, 0x28, 0xa6, 0x4c, 0x29, 0xe1, 0xe3, 0xa0, 0x2a, 0x3e, 0xb5, 0x3d, 0x3a, 0xf4, 0xa8, + 0x4a, 0x19, 0xba, 0x22, 0x6e, 0x5f, 0x0d, 0xaa, 0x3d, 0xcc, 0x50, 0xf5, 0xf6, 0x7f, 0xcc, 0x20, + 0x96, 0xfa, 0x5e, 0xdf, 0x8b, 0x96, 0x6a, 0xb8, 0x9a, 0xef, 0x3e, 0x66, 0xd8, 0x75, 0xb0, 0x3f, + 0x24, 0x2e, 0x53, 0x51, 0xcf, 0x26, 0x2a, 0x9b, 0x8c, 0x30, 0x8d, 0x1f, 0xca, 0xef, 0x38, 0xf0, + 0x45, 0x17, 0x0d, 0x88, 0x83, 0x98, 0xe7, 0x9b, 0x98, 0x35, 0x2e, 0x91, 0xdb, 0xc7, 0xe7, 0xc8, + 0xbe, 0xc2, 0xac, 0x89, 0x18, 0x82, 0x1e, 0xd8, 0x0b, 0x6e, 0x9f, 0x5b, 0xe3, 0x91, 0x83, 0x18, + 0xa6, 0x02, 0x27, 0xe5, 0x2a, 0x85, 0x9a, 0xa4, 0x2c, 0x99, 0x95, 0x90, 0x59, 0x59, 0x30, 0x75, + 0xa2, 0xc1, 0xba, 0xf4, 0xe6, 0xfd, 0x93, 0xcc, 0x7f, 0xef, 0x9f, 0x08, 0x13, 0x34, 0x1c, 0x7c, + 0x27, 0x27, 0x88, 0x64, 0x83, 0x0f, 0xd6, 0x21, 0x14, 0x56, 0x40, 0xb8, 0x47, 0x31, 0x9b, 0x0f, + 0x59, 0xc4, 0x11, 0xb2, 0x12, 0x57, 0xc9, 0x1b, 0xc5, 0x78, 0x3f, 0x1e, 0xd4, 0x1d, 0xf8, 0x25, + 0x00, 0x74, 0x80, 0xe8, 0xa5, 0x85, 0xec, 0x2b, 0x2a, 0xe4, 0xa4, 0x5c, 0x65, 0xc7, 0xd8, 0x89, + 0x76, 0x0e, 0xed, 0x2b, 0x2a, 0x7b, 0xe0, 0xd1, 0x26, 0x65, 0x14, 0x1a, 0x20, 0x3f, 0x20, 0x94, + 0xcd, 0x95, 0x7c, 0xab, 0x6c, 0xf6, 0x5e, 0xd9, 0x66, 0x4f, 0x3d, 0x1f, 0x2a, 0x34, 0x22, 0x2e, + 0xf9, 0x07, 0x50, 0xea, 0x9a, 0x8d, 0x53, 0xc4, 0xc6, 0x3e, 0x76, 0x56, 0x2c, 0x4c, 0x53, 0xc4, + 0xa5, 0x29, 0x92, 0xff, 0xe6, 0xc0, 0xae, 0x19, 0x0a, 0x58, 0x41, 0x1b, 0x60, 0x67, 0xe1, 0x51, + 0x04, 0x2b, 0xd4, 0xc4, 0xcd, 0xc6, 0xd7, 0x85, 0xb9, 0xe5, 0xfc, 0x1d, 0xcb, 0x65, 0x63, 0x49, + 0x73, 0x0f, 0x8f, 0xeb, 0x00, 0x10, 0xf7, 0xc2, 0x47, 0x36, 0x23, 0x9e, 0x2b, 0xe4, 0x24, 0xae, + 0x52, 0xac, 0xc9, 0x4a, 0xdc, 0x46, 0xe5, 0xb6, 0x7d, 0xf3, 0x36, 0x2a, 0xfa, 0x62, 0xd2, 0x58, + 0x41, 0xc9, 0x5f, 0x81, 0x87, 0x73, 0x53, 0x3a, 0x6e, 0xcf, 0x73, 0x1d, 0xe2, 0xf6, 0xcf, 0x46, + 0x14, 0xf2, 0x20, 0x47, 0x9c, 0xb8, 0x4b, 0x79, 0x23, 0x5c, 0xca, 0x7f, 0x66, 0x01, 0x6c, 0x78, + 0x2e, 0x1d, 0x0f, 0xb1, 0xbf, 0xe2, 0xc0, 0x11, 0xc8, 0x87, 0x95, 0x8d, 0xc4, 0x17, 0x6b, 0xb5, + 0x6d, 0x59, 0x25, 0xd1, 0xed, 0xc9, 0x08, 0x1b, 0x11, 0x1e, 0xbe, 0x00, 0xbb, 0x74, 0xdd, 0xdc, + 0x48, 0x74, 0xa1, 0xf6, 0xf5, 0x36, 0xca, 0x3b, 0x79, 0x1c, 0x67, 0x8c, 0xbb, 0x2c, 0xf0, 0x02, + 0x94, 0x02, 0x6a, 0x27, 0x82, 0x8f, 0xec, 0x2a, 0xd4, 0xbe, 0xd9, 0x5a, 0xae, 0x94, 0xc2, 0x1c, + 0x67, 0x8c, 0x54, 0xbe, 0xd8, 0xb1, 0x57, 0x42, 0x3e, 0x4a, 0x2a, 0x5c, 0xd6, 0x1f, 0x80, 0xbc, + 0x83, 0x18, 0x92, 0x7b, 0xe0, 0xf3, 0xa4, 0xf4, 0x13, 0x42, 0x19, 0x3c, 0x5e, 0x2b, 0xba, 0x72, + 0x3f, 0xf3, 0xd6, 0xea, 0xfd, 0x3a, 0x0b, 0x4a, 0xc9, 0x91, 0x6e, 0xf5, 0x83, 0xe5, 0xf3, 0x72, + 0x53, 0x3e, 0xcf, 0xee, 0x91, 0x4f, 0xb7, 0xfa, 0x11, 0x13, 0x5a, 0xe4, 0xf1, 0x0f, 0x07, 0xf6, + 0x12, 0x2f, 0xf6, 0x91, 0x8f, 0xf2, 0x4f, 0x29, 0x47, 0x79, 0x7f, 0x9b, 0xf2, 0xe5, 0x71, 0x8e, + 0x42, 0x5a, 0x41, 0xef, 0xff, 0xc5, 0xa5, 0x15, 0x2e, 0x1c, 0x83, 0xdf, 0x03, 0xa9, 0x71, 0xd6, + 0x32, 0x3b, 0xa7, 0x9a, 0x61, 0x9d, 0x1f, 0x36, 0x7e, 0xd6, 0xda, 0x56, 0xfb, 0xe5, 0xb9, 0x66, + 0x75, 0x5a, 0xe6, 0xb9, 0xd6, 0xd0, 0x8f, 0x74, 0xad, 0xc9, 0x67, 0xc4, 0xcf, 0xa6, 0x33, 0x69, + 0xaf, 0xe3, 0xd2, 0x11, 0xb6, 0xc9, 0x05, 0xb9, 0xf5, 0x10, 0xaa, 0x40, 0x4c, 0x05, 0x9b, 0x27, + 0x87, 0xe6, 0x31, 0xcf, 0x89, 0xbb, 0xd3, 0x99, 0x54, 0x58, 0x31, 0x16, 0x1e, 0x80, 0x47, 0xa9, + 0x80, 0x30, 0x35, 0x3e, 0x2b, 0x96, 0xa6, 0x33, 0x89, 0xef, 0xde, 0x49, 0x4a, 0xcc, 0xff, 0xf6, + 0x47, 0x39, 0xb3, 0xff, 0x9a, 0x03, 0xc5, 0x75, 0x89, 0xf0, 0x39, 0x78, 0xac, 0xb7, 0x8e, 0x8c, + 0xc3, 0x46, 0x5b, 0x3f, 0x6b, 0xa5, 0xbd, 0xf6, 0xc3, 0xe9, 0x4c, 0xda, 0x5d, 0x82, 0xb4, 0xe1, + 0x88, 0x4d, 0xa0, 0x9a, 0x44, 0x35, 0xcf, 0x3a, 0xf5, 0x13, 0xcd, 0x32, 0xf5, 0x1f, 0x5b, 0x3c, + 0x27, 0x16, 0xa7, 0x33, 0x09, 0x34, 0xbd, 0x71, 0x6f, 0x80, 0x4d, 0xd2, 0x77, 0xe1, 0x3e, 0x10, + 0x92, 0x80, 0x17, 0xad, 0xb6, 0x7e, 0xaa, 0xf1, 0x59, 0xf1, 0xd3, 0xe9, 0x4c, 0xfa, 0xa4, 0xe9, + 0xfd, 0xea, 0x32, 0x32, 0xc4, 0xf1, 0xbb, 0xd6, 0x5b, 0x6f, 0xae, 0xcb, 0xdc, 0xdb, 0xeb, 0x32, + 0xf7, 0xef, 0x75, 0x99, 0xfb, 0xfd, 0xa6, 0x9c, 0x79, 0x7b, 0x53, 0xce, 0xbc, 0xbb, 0x29, 0x67, + 0x7e, 0x79, 0xde, 0x27, 0xec, 0x72, 0xdc, 0x53, 0x6c, 0x6f, 0xa8, 0xce, 0x2f, 0x09, 0xcb, 0x48, + 0x9f, 0x2d, 0x6e, 0x1b, 0xc1, 0x81, 0xfa, 0x2a, 0xba, 0x72, 0x44, 0x1f, 0xff, 0xde, 0x83, 0xe8, + 0xeb, 0x7f, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x69, 0x5d, 0x6d, 0x1f, 0x9a, 0x08, 0x00, + 0x00, } func (m *ValidatorSetChangePacketData) Marshal() (dAtA []byte, err error) { @@ -954,6 +964,11 @@ func (m *ConsumerPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Idx != 0 { + i = encodeVarintCcv(dAtA, i, uint64(m.Idx)) + i-- + dAtA[i] = 0x20 + } if m.Data != nil { { size := m.Data.Size() @@ -1279,6 +1294,9 @@ func (m *ConsumerPacketData) Size() (n int) { if m.Data != nil { n += m.Data.Size() } + if m.Idx != 0 { + n += 1 + sovCcv(uint64(m.Idx)) + } return n } @@ -2036,6 +2054,25 @@ func (m *ConsumerPacketData) Unmarshal(dAtA []byte) error { } m.Data = &ConsumerPacketData_VscMaturedPacketData{v} iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Idx", wireType) + } + m.Idx = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCcv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Idx |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipCcv(dAtA[iNdEx:]) From b7a0d1d4806d65265342f21cc23fc73495c055e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 12:30:31 -0700 Subject: [PATCH 10/38] build(deps): bump google.golang.org/grpc from 1.56.1 to 1.56.2 (#1126) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.56.1 to 1.56.2. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.56.1...v1.56.2) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 036c4d4c46..d639ef5dca 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( golang.org/x/net v0.9.0 // indirect golang.org/x/sys v0.7.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 - google.golang.org/grpc v1.56.1 + google.golang.org/grpc v1.56.2 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 6ef711918b..5bf7c2d676 100644 --- a/go.sum +++ b/go.sum @@ -1790,8 +1790,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= +google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= From efd69b0ff11a15cd5108e27d1f575badefea59a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 12:59:07 -0700 Subject: [PATCH 11/38] build(deps): bump semver from 6.3.0 to 6.3.1 in /tests/difference/core/model (#1129) build(deps): bump semver in /tests/difference/core/model Bumps [semver](https://github.com/npm/node-semver) from 6.3.0 to 6.3.1. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/v6.3.1/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v6.3.0...v6.3.1) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> --- tests/difference/core/model/yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/difference/core/model/yarn.lock b/tests/difference/core/model/yarn.lock index 2f35241e52..a708e2fa11 100644 --- a/tests/difference/core/model/yarn.lock +++ b/tests/difference/core/model/yarn.lock @@ -2498,16 +2498,16 @@ safe-buffer@~5.1.1: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== semver@7.x, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== shallow-clone@^3.0.0: version "3.0.1" From 3f3ba9c21fec75c349e0b4f2cfaf652e5966a904 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 13:23:19 -0700 Subject: [PATCH 12/38] build(deps): bump semver from 5.7.1 to 5.7.2 in /docs (#1141) Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> --- docs/package-lock.json | 144 ++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 7d86ce0b2e..385c2a96d6 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -236,9 +236,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -312,9 +312,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -372,9 +372,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1624,9 +1624,9 @@ } }, "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1836,9 +1836,9 @@ } }, "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -2799,9 +2799,9 @@ } }, "node_modules/@mdx-js/mdx/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "bin": { "semver": "bin/semver" } @@ -4045,9 +4045,9 @@ } }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -7770,9 +7770,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -8395,9 +8395,9 @@ } }, "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -10060,9 +10060,9 @@ } }, "node_modules/remark-mdx/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "bin": { "semver": "bin/semver" } @@ -10511,9 +10511,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -10536,9 +10536,9 @@ } }, "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -12934,9 +12934,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -12993,9 +12993,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -13037,9 +13037,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -13849,9 +13849,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -14006,9 +14006,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -14733,9 +14733,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" }, "source-map": { "version": "0.5.7", @@ -15706,9 +15706,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -18383,9 +18383,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -18813,9 +18813,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -19957,9 +19957,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" }, "source-map": { "version": "0.5.7", @@ -20272,9 +20272,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" }, @@ -20303,9 +20303,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, From d77fbf4f49c99b102b072fdf95d35e00ebbf0825 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Mon, 17 Jul 2023 09:18:24 -0700 Subject: [PATCH 13/38] fix!: proper deletion of pending packets (#1146) * Update relay.go * expectation func * reg test * Update CHANGELOG.md --- CHANGELOG.md | 1 + testutil/keeper/expectations.go | 19 +++++++++++++++ x/ccv/consumer/keeper/relay.go | 10 ++++---- x/ccv/consumer/keeper/relay_test.go | 36 +++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c017fc5b1..7109f43bbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Add an entry to the unreleased section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a release. +* (fix!) proper deletion of pending packets [#1146](https://github.com/cosmos/interchain-security/pull/1146) * (feat!) optimize pending packets storage on consumer, with migration! [#1037](https://github.com/cosmos/interchain-security/pull/1037) ## v3.1.0 diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 1e59085d23..8a231f4760 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -14,6 +14,7 @@ import ( channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v3/x/ccv/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" @@ -139,3 +140,21 @@ func ExpectGetCapabilityMock(ctx sdk.Context, mocks MockedKeepers, times int) *g ctx, host.PortPath(ccv.ConsumerPortID), ).Return(nil, true).Times(times) } + +func GetMocksForSendIBCPacket(ctx sdk.Context, mocks MockedKeepers, channelID string, times int) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel(ctx, types.ConsumerPortID, + "consumerCCVChannelID").Return(channeltypes.Channel{}, true).Times(times), + mocks.MockScopedKeeper.EXPECT().GetCapability(ctx, + host.ChannelCapabilityPath(types.ConsumerPortID, "consumerCCVChannelID")).Return( + capabilitytypes.NewCapability(1), true).Times(times), + mocks.MockChannelKeeper.EXPECT().SendPacket(ctx, + capabilitytypes.NewCapability(1), + types.ConsumerPortID, + "consumerCCVChannelID", + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return(uint64(888), nil).Times(times), + } +} diff --git a/x/ccv/consumer/keeper/relay.go b/x/ccv/consumer/keeper/relay.go index 073a1d3996..120c0218c2 100644 --- a/x/ccv/consumer/keeper/relay.go +++ b/x/ccv/consumer/keeper/relay.go @@ -186,6 +186,7 @@ func (k Keeper) SendPackets(ctx sdk.Context) { } pending := k.GetPendingPackets(ctx) + idxsForDeletion := []uint64{} for _, p := range pending { // send packet over IBC @@ -203,18 +204,19 @@ func (k Keeper) SendPackets(ctx sdk.Context) { // IBC client is expired! // leave the packet data stored to be sent once the client is upgraded k.Logger(ctx).Info("IBC client is expired, cannot send IBC packet; leaving packet data stored:", "type", p.Type.String()) - return + break } // Not able to send packet over IBC! // Leave the packet data stored for the sent to be retried in the next block. // Note that if VSCMaturedPackets are not sent for long enough, the provider // will remove the consumer anyway. k.Logger(ctx).Error("cannot send IBC packet; leaving packet data stored:", "type", p.Type.String(), "err", err.Error()) - return + break } + idxsForDeletion = append(idxsForDeletion, p.Idx) } - - k.DeleteAllPendingDataPackets(ctx) + // Delete pending packets that were successfully sent and did not return an error from SendIBCPacket + k.DeletePendingDataPackets(ctx, idxsForDeletion...) } // OnAcknowledgementPacket executes application logic for acknowledgments of sent VSCMatured and Slash packets diff --git a/x/ccv/consumer/keeper/relay_test.go b/x/ccv/consumer/keeper/relay_test.go index 48ad7f5ab9..6dbebe273f 100644 --- a/x/ccv/consumer/keeper/relay_test.go +++ b/x/ccv/consumer/keeper/relay_test.go @@ -307,3 +307,39 @@ func TestSendPacketsFailure(t *testing.T) { consumerKeeper.SendPackets(ctx) require.Equal(t, 3, len(consumerKeeper.GetPendingPackets(ctx))) } + +// Regression test for https://github.com/cosmos/interchain-security/issues/1145 +func TestSendPacketsDeletion(t *testing.T) { + // Keeper setup + consumerKeeper, ctx, ctrl, mocks := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + consumerKeeper.SetProviderChannel(ctx, "consumerCCVChannelID") + consumerKeeper.SetParams(ctx, consumertypes.DefaultParams()) + + // Queue two pending packets + consumerKeeper.AppendPendingPacket(ctx, types.SlashPacket, &types.ConsumerPacketData_SlashPacketData{ // Slash appears first + SlashPacketData: &types.SlashPacketData{ + Validator: abci.Validator{}, + ValsetUpdateId: 88, + Infraction: stakingtypes.Infraction_INFRACTION_DOWNTIME, + }, + }) + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: &types.VSCMaturedPacketData{ + ValsetUpdateId: 90, + }, + }) + + // Get mocks for a successful SendPacket call that does NOT return an error + expectations := testkeeper.GetMocksForSendIBCPacket(ctx, mocks, "consumerCCVChannelID", 1) + // Append mocks for a failed SendPacket call, which returns an error + expectations = append(expectations, mocks.MockChannelKeeper.EXPECT().GetChannel(ctx, types.ConsumerPortID, + "consumerCCVChannelID").Return(channeltypes.Channel{}, false).Times(1)) + gomock.InOrder(expectations...) + + consumerKeeper.SendPackets(ctx) + + // Expect the first successfully sent packet to be popped from queue + require.Equal(t, 1, len(consumerKeeper.GetPendingPackets(ctx))) + require.Equal(t, types.VscMaturedPacket, consumerKeeper.GetPendingPackets(ctx)[0].Type) +} From d8373547507384e3479fc12ccd2de77ddc74383e Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Mon, 17 Jul 2023 20:38:25 +0200 Subject: [PATCH 14/38] docs: fix typos (#1144) * Fix: typo * Fix: typo * Fix: typo * Fix: typos * Fix: typo * Fix: typos * Fix: typos --------- Co-authored-by: MSalopek Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> --- docs/docs/adrs/adr-template.md | 2 +- docs/docs/consumer-development/consumer-chain-governance.md | 2 +- docs/docs/consumer-development/onboarding.md | 6 +++--- docs/docs/features/key-assignment.md | 2 +- docs/docs/features/proposals.md | 4 ++-- docs/docs/features/slashing.md | 4 ++-- docs/docs/introduction/terminology.md | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/docs/adrs/adr-template.md b/docs/docs/adrs/adr-template.md index 2e085330df..b9e3af1678 100644 --- a/docs/docs/adrs/adr-template.md +++ b/docs/docs/adrs/adr-template.md @@ -36,6 +36,6 @@ If the proposed change will be large, please also indicate a way to do the chang ## References -> Are there any relevant PR comments, issues that led up to this, or articles referrenced for why we made the given design choice? If so link them here! +> Are there any relevant PR comments, issues that led up to this, or articles referenced for why we made the given design choice? If so link them here! * {reference link} diff --git a/docs/docs/consumer-development/consumer-chain-governance.md b/docs/docs/consumer-development/consumer-chain-governance.md index ca99286d93..c8586efe3a 100644 --- a/docs/docs/consumer-development/consumer-chain-governance.md +++ b/docs/docs/consumer-development/consumer-chain-governance.md @@ -16,7 +16,7 @@ For an example, see the [Democracy Consumer](https://github.com/cosmos/interchai ## CosmWasm -There several great DAO and governance frameworks written as CosmWasm contracts. These can be used as the main governance system for a consumer chain. Actions triggered by the CosmWasm governance contracts are able to affect parameters and trigger actions on the consumer chain. +There are several great DAO and governance frameworks written as CosmWasm contracts. These can be used as the main governance system for a consumer chain. Actions triggered by the CosmWasm governance contracts are able to affect parameters and trigger actions on the consumer chain. For an example, see [Neutron](https://github.com/neutron-org/neutron/). diff --git a/docs/docs/consumer-development/onboarding.md b/docs/docs/consumer-development/onboarding.md index 89c5f32dc5..dc870b76de 100644 --- a/docs/docs/consumer-development/onboarding.md +++ b/docs/docs/consumer-development/onboarding.md @@ -20,7 +20,7 @@ To help validators and other node runners onboard onto your chain, please prepar This should include (at minimum): -- [ ] genesis.json witout CCV data (before the propsal passes) +- [ ] genesis.json without CCV data (before the proposal passes) - [ ] genesis.json with CCV data (after spawn time passes) - [ ] information about relevant seed/peer nodes you are running - [ ] relayer information (compatible versions) @@ -73,7 +73,7 @@ Example of a consumer chain addition proposal. // will be responsible for starting their consumer chain validator node. "spawn_time": "2023-02-28T20:40:00.000000Z", // Unbonding period for the consumer chain. - // It should should be smaller than that of the provider. + // It should be smaller than that of the provider. "unbonding_period": 86400000000000, // Timeout period for CCV related IBC packets. // Packets are considered timed-out after this interval elapses. @@ -96,7 +96,7 @@ Example of a consumer chain addition proposal. // channel is created on top of the same connection as the CCV channel. // Note that transfer_channel_id is the ID of the channel end on the consumer chain. // it is most relevant for chains performing a sovereign to consumer changeover - // in order to maintan the existing ibc transfer channel + // in order to maintain the existing ibc transfer channel "distribution_transmission_channel": "channel-123" } ``` diff --git a/docs/docs/features/key-assignment.md b/docs/docs/features/key-assignment.md index c68343255c..a44ed8a32a 100644 --- a/docs/docs/features/key-assignment.md +++ b/docs/docs/features/key-assignment.md @@ -49,7 +49,7 @@ gaiad tx provider assign-consensus-key '' --from "}` -Check that the key was assigned correcly by querying the provider: +Check that the key was assigned correctly by querying the provider: ```bash gaiad query provider validator-consumer-key cosmosvalcons1e....3xsj3ayzf4uv6 diff --git a/docs/docs/features/proposals.md b/docs/docs/features/proposals.md index b68fd343d6..25664bfc1e 100644 --- a/docs/docs/features/proposals.md +++ b/docs/docs/features/proposals.md @@ -32,7 +32,7 @@ Minimal example: "revision_number": 1, }, // Unbonding period for the consumer chain. - // It should should be smaller than that of the provider. + // It should be smaller than that of the provider. "unbonding_period": 86400000000000, // Timeout period for CCV related IBC packets. // Packets are considered timed-out after this interval elapses. @@ -44,7 +44,7 @@ Minimal example: "genesis_hash": "d86d756e10118e66e6805e9cc476949da2e750098fcc7634fd0cc77f57a0b2b0", "binary_hash": "376cdbd3a222a3d5c730c9637454cd4dd925e2f9e2e0d0f3702fc922928583f1" // relevant for chains performing a sovereign to consumer changeover - // in order to maintan the existing ibc transfer channel + // in order to maintain the existing ibc transfer channel "distribution_transmission_channel": "channel-123" } ``` diff --git a/docs/docs/features/slashing.md b/docs/docs/features/slashing.md index a28b16e8c2..4c74153b15 100644 --- a/docs/docs/features/slashing.md +++ b/docs/docs/features/slashing.md @@ -3,7 +3,7 @@ sidebar_position: 4 --- # Consumer Initiated Slashing -A consumer chain is essentially a regular Cosmos-SDK based chain that uses the interchain security module to achieve economic security by stake deposited on the provider chain, instead of it's own chain. +A consumer chain is essentially a regular Cosmos-SDK based chain that uses the interchain security module to achieve economic security by stake deposited on the provider chain, instead of its own chain. In essence, provider chain and consumer chains are different networks (different infrastructures) that are bound together by the provider's validator set. By being bound to the provider's validator set, a consumer chain inherits the economic security guarantees of the provider chain (in terms of total stake). To maintain the proof of stake model, the consumer chain is able to send evidence of infractions (double signing and downtime) to the provider chain so the offending validators can be penalized. @@ -17,7 +17,7 @@ reported by consumer chains are acted upon on the provider as soon as the provid Instead of slashing, the provider will only jail offending validator for the duration of time established in the chain parameters. :::info -Slash throttling (sometimes called jail throttling) mechanism insures that only a fraction of the validator set can be jailed at any one time to prevent malicious consumer chains from harming the provider. +Slash throttling (sometimes called jail throttling) mechanism ensures that only a fraction of the validator set can be jailed at any one time to prevent malicious consumer chains from harming the provider. ::: ## Double-signing (equivocation) diff --git a/docs/docs/introduction/terminology.md b/docs/docs/introduction/terminology.md index 5f6722fbd9..59994353cf 100644 --- a/docs/docs/introduction/terminology.md +++ b/docs/docs/introduction/terminology.md @@ -8,7 +8,7 @@ You may have heard of one or multiple buzzwords thrown around in the cosmos and ## Shared Security -Shared security is a family of technologies that include optimistic rollups, zk-rollups, sharding and Interchain Security. Ie. any protocol or technology that can allow one blockchain to lend/share it's proof-of-stake security with another blockchain or off-chain process. +Shared security is a family of technologies that include optimistic rollups, zk-rollups, sharding and Interchain Security. Ie. any protocol or technology that can allow one blockchain to lend/share its proof-of-stake security with another blockchain or off-chain process. ## Interchain Security From d67d1bca18440733982bd0680193f31eac2bd59d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 14:33:45 -0700 Subject: [PATCH 15/38] build(deps): bump bufbuild/buf-setup-action from 1.23.1 to 1.24.0 (#1151) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.23.1 to 1.24.0. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.23.1...v1.24.0) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 08748fe079..d79db5af70 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.23.1 + - uses: bufbuild/buf-setup-action@v1.24.0 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 0049c05d20..9791a871c0 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.23.1 + - uses: bufbuild/buf-setup-action@v1.24.0 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From 781eefb267882e70574be86d2f7f4d6f5abcf75f Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Mon, 17 Jul 2023 14:57:28 -0700 Subject: [PATCH 16/38] chore: add release/v3.1.x targets for bots (#1140) update Co-authored-by: MSalopek --- .github/dependabot.yml | 10 ++++++++++ .mergify.yml | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ec9119084e..65ff156753 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -37,3 +37,13 @@ updates: open-pull-requests-limit: 0 labels: - dependencies + + - package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + target-branch: "release/v3.1.x" + # Only allow automated security-related dependency updates on release branches. + open-pull-requests-limit: 0 + labels: + - dependencies diff --git a/.mergify.yml b/.mergify.yml index 63b8b61550..e08625bf28 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -26,3 +26,11 @@ pull_request_rules: backport: branches: - release/v3.0.x + - name: Backport patches to the release/v3.1.x branch + conditions: + - base=main + - label=A:backport/v3.1.x + actions: + backport: + branches: + - release/v3.1.x From 13468a94ae53d310d1620ecfa79076710af24e34 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Tue, 18 Jul 2023 15:40:55 +0200 Subject: [PATCH 17/38] fix: update broken .dependabot.yml (#1156) --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 65ff156753..647408015a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -38,7 +38,7 @@ updates: labels: - dependencies - - package-ecosystem: gomod + - package-ecosystem: gomod directory: "/" schedule: interval: daily From 82cdd910d92dd9b09a597b484e83e6a7f033e743 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Tue, 18 Jul 2023 08:05:01 -0700 Subject: [PATCH 18/38] docs: ADR for standalone to consumer changeover (#1111) * Create adr-010-standalone-changeover.md * Update adr-010-standalone-changeover.md * Update adr-010-standalone-changeover.md * change order * permalinks --------- Co-authored-by: MSalopek --- .../adrs/adr-010-standalone-changeover.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/docs/adrs/adr-010-standalone-changeover.md diff --git a/docs/docs/adrs/adr-010-standalone-changeover.md b/docs/docs/adrs/adr-010-standalone-changeover.md new file mode 100644 index 0000000000..17c192d1fe --- /dev/null +++ b/docs/docs/adrs/adr-010-standalone-changeover.md @@ -0,0 +1,55 @@ +--- +sidebar_position: 11 +title: Standalone to Consumer Changeover +--- +## ADR 010: Standalone to Consumer Changeover + +## Changelog + +* 6/30/23: Feature completed, first draft of ADR. + +## Status + +Implemented + +## Context + +[Stride](https://github.com/Stride-Labs/stride) will be the first consumer to "changeover" from a standalone cosmos blockchain, to a consumer chain secured by the Cosmos Hub. This document will outline the changes made to the replicated security protocol to support this changeover process. + +## Decision + +### Process + +Prior to the changeover, the consumer chain will have an existing staking keeper and validator set, these may be referred to as the "standalone staking keeper" and "standalone validator set" respectively. + +The first step in the changeover process is to submit a ConsumerAdditionProposal. If the proposal passes, the provider will create a new IBC client for the consumer at spawn time, with the provider's validator set. A consumer genesis will also be constructed by the provider for validators to query. Within this consumer genesis contains the initial validator set for the consumer to apply after the changeover. + +Next, the standalone consumer chain runs an upgrade which adds the CCV module, and is properly setup to execute changeover logic. + +The consumer upgrade height must be reached after the provider has created the new IBC client. Any replicated security validators who will run the consumer, but are not a part of the sovereign validator set, must sync up a full node before the consumer upgrade height is reached. The disc state of said full node will be used to run the consumer chain after the changeover has completed. + +The meat of the changeover logic is that the consumer chain validator set is updated to that which was specified by the provider via the queried consumer genesis. Validators which were a part of the old set, but not the new set, are given zero voting power. Once these validator updates are given to Comet, the set is committed, and in effect 2 blocks later (see [FirstConsumerHeight](https://github.com/cosmos/interchain-security/blob/f10e780df182158d95a30f7cf94588b2d0479309/x/ccv/consumer/keeper/changeover.go#L19)). + +A relayer then establishes the new IBC connection between the provider and consumer. The CCV channel handshake is started on top of this connection. Once the CCV channel is established and VSC packets are being relayed, the consumer chain is secured by the provider. + +### Changes to CCV Protocol + +* Consumer Genesis state is updated to include a `PreCCV` boolean. When this boolean is set true in the consumer genesis JSON, [special logic](https://github.com/cosmos/interchain-security/blob/f10e780df182158d95a30f7cf94588b2d0479309/x/ccv/consumer/keeper/changeover.go) is executed on InitGenesis to trigger the changeover process on the consumer's first endblocker after the upgrade which adds the CCV module. Note that InitGenesis is not automatically called during chain upgrades, so the consumer must manually call the consumer's InitGenesis method in an upgrade handler. +* The `ConsumerAdditionProposal` type is updated to include a `DistributionTransmissionChannel` field. This field allows the consumer to use an existing IBC transfer channel to send rewards as a part of the CCV protocol. Consumers that're not changing over from a standalone chain will leave this field blank, indicating that a new transfer channel should be created on top of the same connection as the CCV channel. +* The CCV consumer keeper is updated to contain an optional reference to the standalone staking keeper. The standalone staking keeper is used to slash for infractions that happened before the changeover was completed. Ie. any infraction from a block height before the changeover, that is submitted after the changeover, will call the standalone staking keeper's slash method. Note that a changeover consumer's standalone staking keeper becomes a democracy module keeper, so it is possible for a governance token to be slashed. + +## Consequences + +### Positive + +* Existing cosmos chains are now able to onboard over to a consumer chain secured by a provider. +* The previous staking keepers for such chains can be transitioned to democracy staking module keepers. + +### Negative + +* The delineation between different types of consumers in this repo becomes less clear. Ie. there is code in the [democracy consumer's app.go](https://github.com/cosmos/interchain-security/blob/f10e780df182158d95a30f7cf94588b2d0479309/app/consumer-democracy/app.go) that only applies to a previously standalone chain, but that file also serves as the base for a normal democracy consumer launched with RS from genesis. + +## References + +* EPIC: Standalone to Consumer Changeover [#756](https://github.com/cosmos/interchain-security/issues/756) +* [Changeover diagram from Stride](https://app.excalidraw.com/l/9UFOCMAZLAI/5EVLj0WJcwt) From 3bb50053673ddfbd1a72be0ade92ac2069534312 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 08:32:51 -0700 Subject: [PATCH 19/38] build(deps): bump cosmossdk.io/errors from 1.0.0-beta.7 to 1.0.0 (#1154) Bumps [cosmossdk.io/errors](https://github.com/cosmos/cosmos-sdk) from 1.0.0-beta.7 to 1.0.0. - [Release notes](https://github.com/cosmos/cosmos-sdk/releases) - [Changelog](https://github.com/cosmos/cosmos-sdk/blob/main/CHANGELOG.md) - [Commits](https://github.com/cosmos/cosmos-sdk/compare/errors/v1.0.0-beta.7...log/v1.0.0) --- updated-dependencies: - dependency-name: cosmossdk.io/errors dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Co-authored-by: Marius Poke --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index d639ef5dca..7947a36e66 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cosmos/interchain-security/v3 go 1.20 require ( - cosmossdk.io/errors v1.0.0-beta.7 + cosmossdk.io/errors v1.0.0 cosmossdk.io/math v1.0.1 github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.8.0 @@ -22,10 +22,10 @@ require ( github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.4 github.com/tidwall/gjson v1.14.4 - golang.org/x/crypto v0.8.0 // indirect + golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sys v0.10.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 google.golang.org/grpc v1.56.2 google.golang.org/protobuf v1.31.0 @@ -153,8 +153,8 @@ require ( go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/term v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.114.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 5bf7c2d676..26ceadb4ea 100644 --- a/go.sum +++ b/go.sum @@ -198,8 +198,8 @@ cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= cosmossdk.io/core v0.5.1/go.mod h1:KZtwHCLjcFuo0nmDc24Xy6CRNEL9Vl/MeimQ2aC7NLE= cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw= cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU= -cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w= -cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE= +cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= +cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= cosmossdk.io/log v1.1.0 h1:v0ogPHYeTzPcBTcPR1A3j1hkei4pZama8kz8LKlCMv0= cosmossdk.io/log v1.1.0/go.mod h1:6zjroETlcDs+mm62gd8Ig7mZ+N+fVOZS91V17H+M4N4= cosmossdk.io/math v1.0.1 h1:Qx3ifyOPaMLNH/89WeZFH268yCvU4xEcnPLu3sJqPPg= @@ -1208,8 +1208,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1319,8 +1319,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1470,14 +1470,14 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1488,8 +1488,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 9cac44e9caec97fa04b2ee1526aeec3e5c54b93b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 09:49:16 -0700 Subject: [PATCH 20/38] build(deps): bump JamesIves/github-pages-deploy-action from 4.4.2 to 4.4.3 (#1152) build(deps): bump JamesIves/github-pages-deploy-action Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.4.2 to 4.4.3. - [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases) - [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.4.2...v4.4.3) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Co-authored-by: Marius Poke --- .github/workflows/deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 87b4ced180..0c7dff60c5 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -40,7 +40,7 @@ jobs: make build-docs - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@v4.4.2 + uses: JamesIves/github-pages-deploy-action@v4.4.3 with: branch: gh-pages folder: ~/output From 2f62e7d914ffafad2070cc8774c2e19269b57f97 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Tue, 18 Jul 2023 10:10:58 -0700 Subject: [PATCH 21/38] fix!: revert recent consumer packet data changes (#1150) * revert changes * lint * Update CHANGELOG.md * wrapper type --- CHANGELOG.md | 1 + proto/interchain_security/ccv/v1/ccv.proto | 1 - x/ccv/consumer/keeper/genesis_test.go | 4 - x/ccv/consumer/keeper/keeper.go | 40 ++++-- x/ccv/consumer/keeper/keeper_test.go | 11 +- x/ccv/consumer/keeper/migration_test.go | 5 - x/ccv/consumer/keeper/relay.go | 2 +- x/ccv/types/ccv.go | 3 +- x/ccv/types/ccv.pb.go | 142 ++++++++------------- 9 files changed, 92 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7109f43bbf..2a5c14c6e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Add an entry to the unreleased section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a release. +* (fix!) revert consumer packet data changes from #1037 [#1150](https://github.com/cosmos/interchain-security/pull/1150) * (fix!) proper deletion of pending packets [#1146](https://github.com/cosmos/interchain-security/pull/1146) * (feat!) optimize pending packets storage on consumer, with migration! [#1037](https://github.com/cosmos/interchain-security/pull/1037) diff --git a/proto/interchain_security/ccv/v1/ccv.proto b/proto/interchain_security/ccv/v1/ccv.proto index b8ad8ee6d0..adf8f418de 100644 --- a/proto/interchain_security/ccv/v1/ccv.proto +++ b/proto/interchain_security/ccv/v1/ccv.proto @@ -64,7 +64,6 @@ message ConsumerPacketData { SlashPacketData slashPacketData = 2; VSCMaturedPacketData vscMaturedPacketData = 3; } - uint64 idx = 4; } diff --git a/x/ccv/consumer/keeper/genesis_test.go b/x/ccv/consumer/keeper/genesis_test.go index 5058f6ff5c..649505da0c 100644 --- a/x/ccv/consumer/keeper/genesis_test.go +++ b/x/ccv/consumer/keeper/genesis_test.go @@ -262,16 +262,12 @@ func TestExportGenesis(t *testing.T) { Data: &ccv.ConsumerPacketData_SlashPacketData{ SlashPacketData: ccv.NewSlashPacketData(abciValidator, vscID, stakingtypes.Infraction_INFRACTION_DOWNTIME), }, - Idx: 0, }, { Type: ccv.VscMaturedPacket, Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: ccv.NewVSCMaturedPacketData(vscID), }, - // This idx is a part of the expected genesis state. - // If the keeper is correctly storing consumer packet data, indexes should be populated. - Idx: 1, }, }, } diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index cfff31b367..e8b1cb793e 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -606,9 +606,29 @@ func (k Keeper) getAndIncrementPendingPacketsIdx(ctx sdk.Context) (toReturn uint return toReturn } -// GetPendingPackets returns ALL the pending CCV packets from the store +// GetPendingPackets returns ALL the pending CCV packets from the store without indexes. func (k Keeper) GetPendingPackets(ctx sdk.Context) []ccv.ConsumerPacketData { - var packets []ccv.ConsumerPacketData + ppWithIndexes := k.GetAllPendingPacketsWithIdx(ctx) + var ppList []ccv.ConsumerPacketData + for _, ppWithIndex := range ppWithIndexes { + ppList = append(ppList, ppWithIndex.ConsumerPacketData) + } + return ppList +} + +// ConsumerPacketDataWithIdx is a wrapper around ConsumerPacketData +// that also stores the index of the packet in the pending packets queue. +// +// Note this type is a shim to avoid changing the schema of ConsumerPacketData and breaking the wire. +type ConsumerPacketDataWithIdx struct { + ccv.ConsumerPacketData // Struct embedding + Idx uint64 +} + +// GetAllPendingPacketsWithIdx returns ALL pending consumer packet data from the store +// with indexes relevant to the pending packets queue. +func (k Keeper) GetAllPendingPacketsWithIdx(ctx sdk.Context) []ConsumerPacketDataWithIdx { + packets := []ConsumerPacketDataWithIdx{} store := ctx.KVStore(k.storeKey) // Note: PendingDataPacketsBytePrefix is the correct prefix, NOT PendingDataPacketsByteKey. // See consistency with PendingDataPacketsKey(). @@ -622,7 +642,12 @@ func (k Keeper) GetPendingPackets(ctx sdk.Context) []ccv.ConsumerPacketData { // An error here would indicate something is very wrong, panic(fmt.Errorf("failed to unmarshal pending data packet: %w", err)) } - packets = append(packets, packet) + packetWithIdx := ConsumerPacketDataWithIdx{ + ConsumerPacketData: packet, + // index stored in key after prefix, see PendingDataPacketsKey() + Idx: sdk.BigEndianToUint64(iterator.Key()[1:]), + } + packets = append(packets, packetWithIdx) } return packets } @@ -652,13 +677,10 @@ func (k Keeper) DeleteAllPendingDataPackets(ctx sdk.Context) { // AppendPendingPacket enqueues the given data packet to the end of the pending data packets queue func (k Keeper) AppendPendingPacket(ctx sdk.Context, packetType ccv.ConsumerPacketDataType, data ccv.ExportedIsConsumerPacketData_Data) { - cpd := ccv.NewConsumerPacketData( - packetType, - data, - k.getAndIncrementPendingPacketsIdx(ctx), - ) - key := types.PendingDataPacketsKey(cpd.Idx) + idx := k.getAndIncrementPendingPacketsIdx(ctx) // for FIFO queue + key := types.PendingDataPacketsKey(idx) store := ctx.KVStore(k.storeKey) + cpd := ccv.NewConsumerPacketData(packetType, data) bz, err := cpd.Marshal() if err != nil { // This should never happen diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index 5802c9d590..269c60d9c5 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -325,14 +325,12 @@ func TestPendingPackets(t *testing.T) { Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: ccv.NewVSCMaturedPacketData(1), }, - Idx: 0, // Note these are expected idxs, we don't pass this data to the keeper }, { Type: ccv.VscMaturedPacket, Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: ccv.NewVSCMaturedPacketData(2), }, - Idx: 1, }, { Type: ccv.SlashPacket, @@ -343,14 +341,12 @@ func TestPendingPackets(t *testing.T) { stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN, ), }, - Idx: 2, }, { Type: ccv.VscMaturedPacket, Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: ccv.NewVSCMaturedPacketData(3), }, - Idx: 3, }, } @@ -376,7 +372,6 @@ func TestPendingPackets(t *testing.T) { Data: &ccv.ConsumerPacketData_SlashPacketData{ SlashPacketData: slashPacket, }, - Idx: 4, }) toAppend := packetData[len(packetData)-1] @@ -391,7 +386,6 @@ func TestPendingPackets(t *testing.T) { Data: &ccv.ConsumerPacketData_VscMaturedPacketData{ VscMaturedPacketData: vscMaturedPacket, }, - Idx: 5, }) toAppend = packetData[len(packetData)-1] consumerKeeper.AppendPendingPacket(ctx, toAppend.Type, toAppend.Data) @@ -404,16 +398,21 @@ func TestPendingPackets(t *testing.T) { consumerKeeper.DeletePendingDataPackets(ctx, 5) storedPacketData = consumerKeeper.GetPendingPackets(ctx) require.Equal(t, packetData[:len(packetData)-1], storedPacketData) + pendingPacketsWithIdx := consumerKeeper.GetAllPendingPacketsWithIdx(ctx) + require.Equal(t, uint64(4), pendingPacketsWithIdx[len(pendingPacketsWithIdx)-1].Idx) // final element should have idx 4 // Delete packet with idx 0 (first index) consumerKeeper.DeletePendingDataPackets(ctx, 0) storedPacketData = consumerKeeper.GetPendingPackets(ctx) require.Equal(t, packetData[1:len(packetData)-1], storedPacketData) + pendingPacketsWithIdx = consumerKeeper.GetAllPendingPacketsWithIdx(ctx) + require.Equal(t, uint64(1), pendingPacketsWithIdx[0].Idx) // first element should have idx 1 // Delete all packets consumerKeeper.DeleteAllPendingDataPackets(ctx) storedPacketData = consumerKeeper.GetPendingPackets(ctx) require.Empty(t, storedPacketData) + require.Empty(t, consumerKeeper.GetAllPendingPacketsWithIdx(ctx)) } // TestVerifyProviderChain tests the VerifyProviderChain method for the consumer keeper diff --git a/x/ccv/consumer/keeper/migration_test.go b/x/ccv/consumer/keeper/migration_test.go index 1e7bc54bdf..7cb87665f0 100644 --- a/x/ccv/consumer/keeper/migration_test.go +++ b/x/ccv/consumer/keeper/migration_test.go @@ -60,9 +60,4 @@ func TestMigrateConsumerPacketData(t *testing.T) { require.Equal(t, uint64(77), obtainedPackets[0].GetSlashPacketData().ValsetUpdateId) require.Equal(t, uint64(88), obtainedPackets[1].GetVscMaturedPacketData().ValsetUpdateId) require.Equal(t, uint64(99), obtainedPackets[2].GetVscMaturedPacketData().ValsetUpdateId) - - // Check that indexes are populated - require.Equal(t, uint64(0), obtainedPackets[0].Idx) - require.Equal(t, uint64(1), obtainedPackets[1].Idx) - require.Equal(t, uint64(2), obtainedPackets[2].Idx) } diff --git a/x/ccv/consumer/keeper/relay.go b/x/ccv/consumer/keeper/relay.go index 120c0218c2..376110f26d 100644 --- a/x/ccv/consumer/keeper/relay.go +++ b/x/ccv/consumer/keeper/relay.go @@ -185,7 +185,7 @@ func (k Keeper) SendPackets(ctx sdk.Context) { return } - pending := k.GetPendingPackets(ctx) + pending := k.GetAllPendingPacketsWithIdx(ctx) idxsForDeletion := []uint64{} for _, p := range pending { diff --git a/x/ccv/types/ccv.go b/x/ccv/types/ccv.go index 453c55d252..91175eab5d 100644 --- a/x/ccv/types/ccv.go +++ b/x/ccv/types/ccv.go @@ -171,10 +171,9 @@ type ExportedIsConsumerPacketData_Data interface { isConsumerPacketData_Data } -func NewConsumerPacketData(cpdType ConsumerPacketDataType, data isConsumerPacketData_Data, idx uint64) ConsumerPacketData { +func NewConsumerPacketData(cpdType ConsumerPacketDataType, data isConsumerPacketData_Data) ConsumerPacketData { return ConsumerPacketData{ Type: cpdType, Data: data, - Idx: idx, } } diff --git a/x/ccv/types/ccv.pb.go b/x/ccv/types/ccv.pb.go index c5eacc2288..aa0f7da10e 100644 --- a/x/ccv/types/ccv.pb.go +++ b/x/ccv/types/ccv.pb.go @@ -368,7 +368,6 @@ type ConsumerPacketData struct { // *ConsumerPacketData_SlashPacketData // *ConsumerPacketData_VscMaturedPacketData Data isConsumerPacketData_Data `protobuf_oneof:"data"` - Idx uint64 `protobuf:"varint,4,opt,name=idx,proto3" json:"idx,omitempty"` } func (m *ConsumerPacketData) Reset() { *m = ConsumerPacketData{} } @@ -448,13 +447,6 @@ func (m *ConsumerPacketData) GetVscMaturedPacketData() *VSCMaturedPacketData { return nil } -func (m *ConsumerPacketData) GetIdx() uint64 { - if m != nil { - return m.Idx - } - return 0 -} - // XXX_OneofWrappers is for the internal use of the proto package. func (*ConsumerPacketData) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -687,61 +679,60 @@ func init() { } var fileDescriptor_68bd5f3242e6f29c = []byte{ - // 849 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0xcf, 0x6e, 0xe2, 0x46, - 0x1c, 0xc6, 0x80, 0x56, 0xcd, 0x50, 0x11, 0x67, 0x96, 0x56, 0x5e, 0x6f, 0xcb, 0x5a, 0xd6, 0x4a, + // 836 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xcf, 0x6e, 0xe2, 0x46, + 0x1c, 0xb6, 0x01, 0xad, 0x9a, 0xa1, 0x22, 0xce, 0x2c, 0xad, 0x58, 0x6f, 0xcb, 0x5a, 0xd6, 0x4a, 0x45, 0xa9, 0xd6, 0x2e, 0x64, 0x0f, 0x55, 0x7b, 0x69, 0x00, 0xa7, 0x71, 0x9b, 0x90, 0xc8, 0x06, - 0x56, 0xdb, 0x8b, 0x35, 0xd8, 0x13, 0x32, 0x0a, 0xd8, 0xc8, 0x33, 0xb8, 0xcb, 0x1b, 0x54, 0x9c, - 0xfa, 0x02, 0x9c, 0x7a, 0xda, 0x27, 0xe8, 0xb9, 0xb7, 0x3d, 0xae, 0xd4, 0xcb, 0x5e, 0xba, 0xaa, - 0x92, 0x37, 0xe8, 0x13, 0x54, 0xb6, 0x09, 0x7f, 0x62, 0x83, 0x14, 0x69, 0xa5, 0x3d, 0x31, 0x8c, - 0x7f, 0xdf, 0x27, 0x7f, 0x7f, 0x46, 0x1e, 0xf0, 0x94, 0xb8, 0x0c, 0xfb, 0xf6, 0x25, 0x22, 0xae, - 0x45, 0xb1, 0x3d, 0xf6, 0x09, 0x9b, 0xa8, 0xb6, 0x1d, 0xa8, 0x41, 0x35, 0xfc, 0x51, 0x46, 0xbe, - 0xc7, 0x3c, 0x28, 0xa6, 0x4c, 0x29, 0xe1, 0xe3, 0xa0, 0x2a, 0x3e, 0xb5, 0x3d, 0x3a, 0xf4, 0xa8, - 0x4a, 0x19, 0xba, 0x22, 0x6e, 0x5f, 0x0d, 0xaa, 0x3d, 0xcc, 0x50, 0xf5, 0xf6, 0x7f, 0xcc, 0x20, - 0x96, 0xfa, 0x5e, 0xdf, 0x8b, 0x96, 0x6a, 0xb8, 0x9a, 0xef, 0x3e, 0x66, 0xd8, 0x75, 0xb0, 0x3f, - 0x24, 0x2e, 0x53, 0x51, 0xcf, 0x26, 0x2a, 0x9b, 0x8c, 0x30, 0x8d, 0x1f, 0xca, 0xef, 0x38, 0xf0, - 0x45, 0x17, 0x0d, 0x88, 0x83, 0x98, 0xe7, 0x9b, 0x98, 0x35, 0x2e, 0x91, 0xdb, 0xc7, 0xe7, 0xc8, - 0xbe, 0xc2, 0xac, 0x89, 0x18, 0x82, 0x1e, 0xd8, 0x0b, 0x6e, 0x9f, 0x5b, 0xe3, 0x91, 0x83, 0x18, - 0xa6, 0x02, 0x27, 0xe5, 0x2a, 0x85, 0x9a, 0xa4, 0x2c, 0x99, 0x95, 0x90, 0x59, 0x59, 0x30, 0x75, - 0xa2, 0xc1, 0xba, 0xf4, 0xe6, 0xfd, 0x93, 0xcc, 0x7f, 0xef, 0x9f, 0x08, 0x13, 0x34, 0x1c, 0x7c, - 0x27, 0x27, 0x88, 0x64, 0x83, 0x0f, 0xd6, 0x21, 0x14, 0x56, 0x40, 0xb8, 0x47, 0x31, 0x9b, 0x0f, - 0x59, 0xc4, 0x11, 0xb2, 0x12, 0x57, 0xc9, 0x1b, 0xc5, 0x78, 0x3f, 0x1e, 0xd4, 0x1d, 0xf8, 0x25, - 0x00, 0x74, 0x80, 0xe8, 0xa5, 0x85, 0xec, 0x2b, 0x2a, 0xe4, 0xa4, 0x5c, 0x65, 0xc7, 0xd8, 0x89, - 0x76, 0x0e, 0xed, 0x2b, 0x2a, 0x7b, 0xe0, 0xd1, 0x26, 0x65, 0x14, 0x1a, 0x20, 0x3f, 0x20, 0x94, - 0xcd, 0x95, 0x7c, 0xab, 0x6c, 0xf6, 0x5e, 0xd9, 0x66, 0x4f, 0x3d, 0x1f, 0x2a, 0x34, 0x22, 0x2e, - 0xf9, 0x07, 0x50, 0xea, 0x9a, 0x8d, 0x53, 0xc4, 0xc6, 0x3e, 0x76, 0x56, 0x2c, 0x4c, 0x53, 0xc4, - 0xa5, 0x29, 0x92, 0xff, 0xe6, 0xc0, 0xae, 0x19, 0x0a, 0x58, 0x41, 0x1b, 0x60, 0x67, 0xe1, 0x51, - 0x04, 0x2b, 0xd4, 0xc4, 0xcd, 0xc6, 0xd7, 0x85, 0xb9, 0xe5, 0xfc, 0x1d, 0xcb, 0x65, 0x63, 0x49, - 0x73, 0x0f, 0x8f, 0xeb, 0x00, 0x10, 0xf7, 0xc2, 0x47, 0x36, 0x23, 0x9e, 0x2b, 0xe4, 0x24, 0xae, - 0x52, 0xac, 0xc9, 0x4a, 0xdc, 0x46, 0xe5, 0xb6, 0x7d, 0xf3, 0x36, 0x2a, 0xfa, 0x62, 0xd2, 0x58, - 0x41, 0xc9, 0x5f, 0x81, 0x87, 0x73, 0x53, 0x3a, 0x6e, 0xcf, 0x73, 0x1d, 0xe2, 0xf6, 0xcf, 0x46, - 0x14, 0xf2, 0x20, 0x47, 0x9c, 0xb8, 0x4b, 0x79, 0x23, 0x5c, 0xca, 0x7f, 0x66, 0x01, 0x6c, 0x78, - 0x2e, 0x1d, 0x0f, 0xb1, 0xbf, 0xe2, 0xc0, 0x11, 0xc8, 0x87, 0x95, 0x8d, 0xc4, 0x17, 0x6b, 0xb5, - 0x6d, 0x59, 0x25, 0xd1, 0xed, 0xc9, 0x08, 0x1b, 0x11, 0x1e, 0xbe, 0x00, 0xbb, 0x74, 0xdd, 0xdc, - 0x48, 0x74, 0xa1, 0xf6, 0xf5, 0x36, 0xca, 0x3b, 0x79, 0x1c, 0x67, 0x8c, 0xbb, 0x2c, 0xf0, 0x02, - 0x94, 0x02, 0x6a, 0x27, 0x82, 0x8f, 0xec, 0x2a, 0xd4, 0xbe, 0xd9, 0x5a, 0xae, 0x94, 0xc2, 0x1c, - 0x67, 0x8c, 0x54, 0xbe, 0xd8, 0xb1, 0x57, 0x42, 0x3e, 0x4a, 0x2a, 0x5c, 0xd6, 0x1f, 0x80, 0xbc, - 0x83, 0x18, 0x92, 0x7b, 0xe0, 0xf3, 0xa4, 0xf4, 0x13, 0x42, 0x19, 0x3c, 0x5e, 0x2b, 0xba, 0x72, - 0x3f, 0xf3, 0xd6, 0xea, 0xfd, 0x3a, 0x0b, 0x4a, 0xc9, 0x91, 0x6e, 0xf5, 0x83, 0xe5, 0xf3, 0x72, - 0x53, 0x3e, 0xcf, 0xee, 0x91, 0x4f, 0xb7, 0xfa, 0x11, 0x13, 0x5a, 0xe4, 0xf1, 0x0f, 0x07, 0xf6, - 0x12, 0x2f, 0xf6, 0x91, 0x8f, 0xf2, 0x4f, 0x29, 0x47, 0x79, 0x7f, 0x9b, 0xf2, 0xe5, 0x71, 0x8e, - 0x42, 0x5a, 0x41, 0xef, 0xff, 0xc5, 0xa5, 0x15, 0x2e, 0x1c, 0x83, 0xdf, 0x03, 0xa9, 0x71, 0xd6, - 0x32, 0x3b, 0xa7, 0x9a, 0x61, 0x9d, 0x1f, 0x36, 0x7e, 0xd6, 0xda, 0x56, 0xfb, 0xe5, 0xb9, 0x66, - 0x75, 0x5a, 0xe6, 0xb9, 0xd6, 0xd0, 0x8f, 0x74, 0xad, 0xc9, 0x67, 0xc4, 0xcf, 0xa6, 0x33, 0x69, - 0xaf, 0xe3, 0xd2, 0x11, 0xb6, 0xc9, 0x05, 0xb9, 0xf5, 0x10, 0xaa, 0x40, 0x4c, 0x05, 0x9b, 0x27, - 0x87, 0xe6, 0x31, 0xcf, 0x89, 0xbb, 0xd3, 0x99, 0x54, 0x58, 0x31, 0x16, 0x1e, 0x80, 0x47, 0xa9, - 0x80, 0x30, 0x35, 0x3e, 0x2b, 0x96, 0xa6, 0x33, 0x89, 0xef, 0xde, 0x49, 0x4a, 0xcc, 0xff, 0xf6, - 0x47, 0x39, 0xb3, 0xff, 0x9a, 0x03, 0xc5, 0x75, 0x89, 0xf0, 0x39, 0x78, 0xac, 0xb7, 0x8e, 0x8c, - 0xc3, 0x46, 0x5b, 0x3f, 0x6b, 0xa5, 0xbd, 0xf6, 0xc3, 0xe9, 0x4c, 0xda, 0x5d, 0x82, 0xb4, 0xe1, - 0x88, 0x4d, 0xa0, 0x9a, 0x44, 0x35, 0xcf, 0x3a, 0xf5, 0x13, 0xcd, 0x32, 0xf5, 0x1f, 0x5b, 0x3c, - 0x27, 0x16, 0xa7, 0x33, 0x09, 0x34, 0xbd, 0x71, 0x6f, 0x80, 0x4d, 0xd2, 0x77, 0xe1, 0x3e, 0x10, - 0x92, 0x80, 0x17, 0xad, 0xb6, 0x7e, 0xaa, 0xf1, 0x59, 0xf1, 0xd3, 0xe9, 0x4c, 0xfa, 0xa4, 0xe9, - 0xfd, 0xea, 0x32, 0x32, 0xc4, 0xf1, 0xbb, 0xd6, 0x5b, 0x6f, 0xae, 0xcb, 0xdc, 0xdb, 0xeb, 0x32, - 0xf7, 0xef, 0x75, 0x99, 0xfb, 0xfd, 0xa6, 0x9c, 0x79, 0x7b, 0x53, 0xce, 0xbc, 0xbb, 0x29, 0x67, - 0x7e, 0x79, 0xde, 0x27, 0xec, 0x72, 0xdc, 0x53, 0x6c, 0x6f, 0xa8, 0xce, 0x2f, 0x09, 0xcb, 0x48, - 0x9f, 0x2d, 0x6e, 0x1b, 0xc1, 0x81, 0xfa, 0x2a, 0xba, 0x72, 0x44, 0x1f, 0xff, 0xde, 0x83, 0xe8, - 0xeb, 0x7f, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x69, 0x5d, 0x6d, 0x1f, 0x9a, 0x08, 0x00, - 0x00, + 0x56, 0xdb, 0x8b, 0x35, 0xd8, 0x13, 0x32, 0x0a, 0xd8, 0xc8, 0x33, 0xb8, 0xe5, 0x0d, 0x2a, 0x4e, + 0x7d, 0x01, 0x4e, 0x55, 0x0f, 0xfb, 0x18, 0xbd, 0xed, 0x71, 0xa5, 0x5e, 0xf6, 0xd2, 0xa8, 0x4a, + 0xde, 0xa0, 0x4f, 0x50, 0xd9, 0xfc, 0xc7, 0x06, 0x29, 0x52, 0xa5, 0xf6, 0x84, 0x19, 0xff, 0xbe, + 0x4f, 0xf3, 0xfd, 0x19, 0x79, 0xc0, 0x73, 0xe2, 0x32, 0xec, 0xdb, 0xd7, 0x88, 0xb8, 0x16, 0xc5, + 0xf6, 0xd0, 0x27, 0x6c, 0xa4, 0xda, 0x76, 0xa0, 0x06, 0xe5, 0xf0, 0x47, 0x19, 0xf8, 0x1e, 0xf3, + 0xa0, 0x98, 0x30, 0xa5, 0x84, 0xaf, 0x83, 0xb2, 0xf8, 0xdc, 0xf6, 0x68, 0xdf, 0xa3, 0x2a, 0x65, + 0xe8, 0x86, 0xb8, 0x5d, 0x35, 0x28, 0x77, 0x30, 0x43, 0xe5, 0xf9, 0xff, 0x29, 0x83, 0x98, 0xef, + 0x7a, 0x5d, 0x2f, 0x7a, 0x54, 0xc3, 0xa7, 0xd9, 0xea, 0x53, 0x86, 0x5d, 0x07, 0xfb, 0x7d, 0xe2, + 0x32, 0x15, 0x75, 0x6c, 0xa2, 0xb2, 0xd1, 0x00, 0xd3, 0xe9, 0x4b, 0xf9, 0x3d, 0x0f, 0x3e, 0x69, + 0xa3, 0x1e, 0x71, 0x10, 0xf3, 0x7c, 0x13, 0xb3, 0xda, 0x35, 0x72, 0xbb, 0xf8, 0x12, 0xd9, 0x37, + 0x98, 0xd5, 0x11, 0x43, 0xd0, 0x03, 0x07, 0xc1, 0xfc, 0xbd, 0x35, 0x1c, 0x38, 0x88, 0x61, 0x5a, + 0xe0, 0xa5, 0x74, 0x29, 0x5b, 0x91, 0x94, 0x25, 0xb3, 0x12, 0x32, 0x2b, 0x0b, 0xa6, 0x56, 0x34, + 0x58, 0x95, 0xde, 0xde, 0x3e, 0xe3, 0xfe, 0xbe, 0x7d, 0x56, 0x18, 0xa1, 0x7e, 0xef, 0x2b, 0x39, + 0x46, 0x24, 0x1b, 0x42, 0xb0, 0x0e, 0xa1, 0xb0, 0x04, 0xc2, 0x35, 0x8a, 0xd9, 0x6c, 0xc8, 0x22, + 0x4e, 0x21, 0x25, 0xf1, 0xa5, 0x8c, 0x91, 0x9b, 0xae, 0x4f, 0x07, 0x75, 0x07, 0x7e, 0x0a, 0x00, + 0xed, 0x21, 0x7a, 0x6d, 0x21, 0xfb, 0x86, 0x16, 0xd2, 0x52, 0xba, 0xb4, 0x67, 0xec, 0x45, 0x2b, + 0xc7, 0xf6, 0x0d, 0x95, 0x3d, 0xf0, 0x64, 0x9b, 0x32, 0x0a, 0x0d, 0x90, 0xe9, 0x11, 0xca, 0x66, + 0x4a, 0xbe, 0x54, 0xb6, 0x7b, 0xaf, 0xec, 0xb2, 0xa7, 0x9a, 0x09, 0x15, 0x1a, 0x11, 0x97, 0xfc, + 0x0d, 0xc8, 0xb7, 0xcd, 0xda, 0x39, 0x62, 0x43, 0x1f, 0x3b, 0x2b, 0x16, 0x26, 0x29, 0xe2, 0x93, + 0x14, 0xc9, 0x7f, 0xf0, 0x60, 0xdf, 0x0c, 0x05, 0xac, 0xa0, 0x0d, 0xb0, 0xb7, 0xf0, 0x28, 0x82, + 0x65, 0x2b, 0xe2, 0x76, 0xe3, 0xab, 0x85, 0x99, 0xe5, 0xc2, 0x86, 0xe5, 0xb2, 0xb1, 0xa4, 0x79, + 0x80, 0xc7, 0x55, 0x00, 0x88, 0x7b, 0xe5, 0x23, 0x9b, 0x11, 0xcf, 0x2d, 0xa4, 0x25, 0xbe, 0x94, + 0xab, 0xc8, 0xca, 0xb4, 0x8d, 0xca, 0xbc, 0x7d, 0xb3, 0x36, 0x2a, 0xfa, 0x62, 0xd2, 0x58, 0x41, + 0xc9, 0x9f, 0x81, 0xc7, 0x33, 0x53, 0x5a, 0x6e, 0xc7, 0x73, 0x1d, 0xe2, 0x76, 0x2f, 0x06, 0x14, + 0x0a, 0x20, 0x4d, 0x9c, 0x69, 0x97, 0x32, 0x46, 0xf8, 0x28, 0xff, 0x96, 0x02, 0xb0, 0xe6, 0xb9, + 0x74, 0xd8, 0xc7, 0xfe, 0x8a, 0x03, 0x27, 0x20, 0x13, 0x56, 0x36, 0x12, 0x9f, 0xab, 0x54, 0x76, + 0x65, 0x15, 0x47, 0x37, 0x47, 0x03, 0x6c, 0x44, 0x78, 0xf8, 0x0a, 0xec, 0xd3, 0x75, 0x73, 0x23, + 0xd1, 0xd9, 0xca, 0xe7, 0xbb, 0x28, 0x37, 0xf2, 0x38, 0xe5, 0x8c, 0x4d, 0x16, 0x78, 0x05, 0xf2, + 0x01, 0xb5, 0x63, 0xc1, 0x47, 0x76, 0x65, 0x2b, 0x5f, 0xec, 0x2c, 0x57, 0x42, 0x61, 0x4e, 0x39, + 0x23, 0x91, 0xaf, 0xfa, 0x08, 0x64, 0x1c, 0xc4, 0x90, 0xdc, 0x01, 0x1f, 0xc7, 0x85, 0x9e, 0x11, + 0xca, 0xe0, 0xe9, 0x5a, 0xad, 0x95, 0x87, 0x59, 0xb5, 0x56, 0xe6, 0x37, 0x29, 0x90, 0x8f, 0x8f, + 0xb4, 0xcb, 0xff, 0x5a, 0x1a, 0xaf, 0xb7, 0xa5, 0xf1, 0xe2, 0x01, 0x69, 0xb4, 0xcb, 0xff, 0x87, + 0x3c, 0xfe, 0xe4, 0xc1, 0x41, 0x6c, 0x63, 0xff, 0xf1, 0xc1, 0xfd, 0x2e, 0xe1, 0xe0, 0x1e, 0xee, + 0x52, 0xbe, 0x3c, 0xbc, 0x51, 0x48, 0x2b, 0xe8, 0xc3, 0xdf, 0xf9, 0xa4, 0xc2, 0x85, 0x63, 0xf0, + 0x6b, 0x20, 0xd5, 0x2e, 0x1a, 0x66, 0xeb, 0x5c, 0x33, 0xac, 0xcb, 0xe3, 0xda, 0xf7, 0x5a, 0xd3, + 0x6a, 0xbe, 0xbe, 0xd4, 0xac, 0x56, 0xc3, 0xbc, 0xd4, 0x6a, 0xfa, 0x89, 0xae, 0xd5, 0x05, 0x4e, + 0xfc, 0x68, 0x3c, 0x91, 0x0e, 0x5a, 0x2e, 0x1d, 0x60, 0x9b, 0x5c, 0x91, 0xb9, 0x87, 0x50, 0x05, + 0x62, 0x22, 0xd8, 0x3c, 0x3b, 0x36, 0x4f, 0x05, 0x5e, 0xdc, 0x1f, 0x4f, 0xa4, 0xec, 0x8a, 0xb1, + 0xf0, 0x08, 0x3c, 0x49, 0x04, 0x84, 0xa9, 0x09, 0x29, 0x31, 0x3f, 0x9e, 0x48, 0x42, 0x7b, 0x23, + 0x29, 0x31, 0xf3, 0xf3, 0xaf, 0x45, 0xee, 0xf0, 0x0d, 0x0f, 0x72, 0xeb, 0x12, 0xe1, 0x4b, 0xf0, + 0x54, 0x6f, 0x9c, 0x18, 0xc7, 0xb5, 0xa6, 0x7e, 0xd1, 0x48, 0xda, 0xf6, 0xe3, 0xf1, 0x44, 0xda, + 0x5f, 0x82, 0xb4, 0xfe, 0x80, 0x8d, 0xa0, 0x1a, 0x47, 0xd5, 0x2f, 0x5a, 0xd5, 0x33, 0xcd, 0x32, + 0xf5, 0x6f, 0x1b, 0x02, 0x2f, 0xe6, 0xc6, 0x13, 0x09, 0xd4, 0xbd, 0x61, 0xa7, 0x87, 0x4d, 0xd2, + 0x75, 0xe1, 0x21, 0x28, 0xc4, 0x01, 0xaf, 0x1a, 0x4d, 0xfd, 0x5c, 0x13, 0x52, 0xe2, 0x87, 0xe3, + 0x89, 0xf4, 0x41, 0xdd, 0xfb, 0xd1, 0x65, 0xa4, 0x8f, 0xa7, 0x7b, 0xad, 0x36, 0xde, 0xde, 0x15, + 0xf9, 0x77, 0x77, 0x45, 0xfe, 0xaf, 0xbb, 0x22, 0xff, 0xcb, 0x7d, 0x91, 0x7b, 0x77, 0x5f, 0xe4, + 0xde, 0xdf, 0x17, 0xb9, 0x1f, 0x5e, 0x76, 0x09, 0xbb, 0x1e, 0x76, 0x14, 0xdb, 0xeb, 0xab, 0xb3, + 0x2b, 0xc1, 0x32, 0xd2, 0x17, 0x8b, 0xbb, 0x45, 0x70, 0xa4, 0xfe, 0x14, 0x5d, 0x30, 0xa2, 0x4f, + 0x7d, 0xe7, 0x51, 0xf4, 0xad, 0x3f, 0xfa, 0x27, 0x00, 0x00, 0xff, 0xff, 0x8f, 0xbf, 0xfc, 0xd2, + 0x88, 0x08, 0x00, 0x00, } func (m *ValidatorSetChangePacketData) Marshal() (dAtA []byte, err error) { @@ -964,11 +955,6 @@ func (m *ConsumerPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Idx != 0 { - i = encodeVarintCcv(dAtA, i, uint64(m.Idx)) - i-- - dAtA[i] = 0x20 - } if m.Data != nil { { size := m.Data.Size() @@ -1294,9 +1280,6 @@ func (m *ConsumerPacketData) Size() (n int) { if m.Data != nil { n += m.Data.Size() } - if m.Idx != 0 { - n += 1 + sovCcv(uint64(m.Idx)) - } return n } @@ -2054,25 +2037,6 @@ func (m *ConsumerPacketData) Unmarshal(dAtA []byte) error { } m.Data = &ConsumerPacketData_VscMaturedPacketData{v} iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Idx", wireType) - } - m.Idx = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowCcv - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Idx |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipCcv(dAtA[iNdEx:]) From 1b44d27f8e9220162839f1bdb166ee93106d5a8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 13:10:17 -0700 Subject: [PATCH 22/38] build(deps): bump bufbuild/buf-setup-action from 1.24.0 to 1.25.0 (#1157) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.24.0 to 1.25.0. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.24.0...v1.25.0) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Marius Poke --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index d79db5af70..9764edd806 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.24.0 + - uses: bufbuild/buf-setup-action@v1.25.0 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 9791a871c0..8f80957d92 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.24.0 + - uses: bufbuild/buf-setup-action@v1.25.0 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From b03f6f0ce07e4d9293e19e83b8f5dd54194e7da4 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Tue, 18 Jul 2023 23:11:37 +0200 Subject: [PATCH 23/38] docs: add changeover procedure (#1123) * docs: add changeover procedure part-1 * docs: add changeover procedure part-2 - onboarding checklist * docs: update terminoloy; add validator instructions stubs * docs: update validator docs * docs: update changeover docs and validator guide * deps: update docusaurus deps * docs: update changeover docs and validator guide * docs: update changeover docs and validator guide --- .../changeover-procedure.md | 236 +++ .../consumer-chain-upgrade-procedure.md | 5 - docs/docs/consumer-development/offboarding.md | 2 +- docs/docs/consumer-development/onboarding.md | 2 +- docs/docs/frequently-asked-questions.md | 2 +- docs/docs/introduction/terminology.md | 15 + docs/docs/validators/changeover-procedure.md | 88 ++ docs/docs/validators/joining-neutron.md | 15 + docs/docs/validators/joining-stride.md | 20 + docs/docs/validators/joining-testnet.md | 6 - docs/docs/validators/withdraw_rewards.md | 4 + .../ics_changeover_timeline_stride.png | Bin 0 -> 395340 bytes docs/package-lock.json | 1357 +++++++++-------- docs/package.json | 10 +- 14 files changed, 1086 insertions(+), 676 deletions(-) create mode 100644 docs/docs/consumer-development/changeover-procedure.md delete mode 100644 docs/docs/consumer-development/consumer-chain-upgrade-procedure.md create mode 100644 docs/docs/validators/changeover-procedure.md create mode 100644 docs/docs/validators/joining-neutron.md create mode 100644 docs/docs/validators/joining-stride.md create mode 100644 docs/figures/ics_changeover_timeline_stride.png diff --git a/docs/docs/consumer-development/changeover-procedure.md b/docs/docs/consumer-development/changeover-procedure.md new file mode 100644 index 0000000000..825a0bdae4 --- /dev/null +++ b/docs/docs/consumer-development/changeover-procedure.md @@ -0,0 +1,236 @@ +--- +sidebar_position: 5 +--- + +# Changeover Procedure + +Chains that were not initially launched as consumers of replicated security can still participate in the protocol and leverage the economic security of the provider chain. The process where a standalone chain transitions to being a replicated consumer chain is called the **changeover procedure** and is part of the interchain security protocol. After the changeover, the new consumer chain will retain all existing state, including the IBC clients, connections and channels already established by the chain. + +The relevant protocol specifications are available below: +* [ICS-28 with existing chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-existing-chains). +* [ADR in ICS repo](../adrs/adr-010-standalone-changeover.md) + +## Overview + +Standalone to consumer changeover procedure can rougly be separated into 4 parts: + +### 1. ConsumerAddition proposal submitted to the `provider` chain +The proposal is equivalent to the "normal" ConsumerAddition proposal submitted by new consumer chains. + +However, here are the most important notes and differences between a new consumer chain and a standalone chain performing a changeover: + +* `chain_id` must be equal to the standalone chain id +* `initial_height` field has additional rules to abide by: + +:::caution +``` +{ +... + "initial_height" : { + // must correspond to current revision number of standalone chain + // e.g. stride-1 => "revision_number": 1 + "revision_number": 1, + + // must correspond to a height that is at least 1 block after the upgrade + // that will add the `consumer` module to the standalone chain + // e.g. "upgrade_height": 100 => "revision_height": 101 + "revision_height": 1, + }, +... +} +``` +RevisionNumber: 0, RevisionHeight: 111 +::: + +* `genesis_hash` can be safely ignored because the chain is already running. A hash of the standalone chain's initial genesis may be used + +* `binary_hash` may not be available ahead of time. All chains performing the changeover go through rigorous testing - if bugs are caught and fixed the hash listed in the proposal may not be the most recent one. + +* `spawn_time` listed in the proposal MUST be before the `upgrade_height` listed in the the upgrade proposal on the standalone chain. +:::caution +`spawn_time` must occur before the `upgrade_height` on the standalone chain is reached becasue the `provider` chain must generate the `ConsumerGenesis` that contains the **validator set** that will be used after the changeover. +::: + +* `unbonding_period` must correspond to the value used on the standalone chain. Otherwise, the clients used for the ccv protocol may be incorrectly initialized. + +* `distribution_transmission_channel` **should be set**. + +:::note +Populating `distribution_transmission_channel` will enable the standalone chain to re-use one of the existing channels to the provider for consumer chain rewards distribution. This will preserve the `ibc denom` that may already be in use. + +If the parameter is not set, a new channel will be created. +::: + +* `ccv_timeout_period` has no important notes + +* `transfer_timeout_period` has no important notes + +* `consumer_redistribution_fraction` has no important notes + +* `blocks_per_distribution_transmission` has no important notes + +* `historical_entries` has no important notes + + +### 2. upgrade proposal on standalone chain + +The standalone chain creates an upgrade proposal to include the `interchain-security/x/ccv/consumer` module. + +:::caution +The upgrade height in the proposal should correspond to a height that is after the `spawn_time` in the consumer addition proposal submitted to the `provider` chain. +::: + +Otherwise, the upgrade is indistinguishable from a regular on-chain upgrade proposal. + +### 3. spawn time is reached + +When the `spawn_time` is reached on the `provider` it will generate a `ConsumerGenesis` that contains the validator set that will supercede the `standalone` validator set. + +This `ConsumerGenesis` must be available on the standalone chain during the on-chain upgrade. + +### 4. standalone chain upgrade + +Performing the on-chain upgrade on the standalone chain will add the `ccv/consumer` module and allow the chain to become a `consumer` of replicated security. + +:::caution +The `ConsumerGenesis` must be exported to a file and placed in the correct folder on the standalone chain before the upgade. + +The file must be placed at the exact specified location, otherwise the upgrade will not be executed correctly. + +Usually the file is placed in `$NODE_HOME/config`, but the file name and the exact directory is dictated by the upgrade code on the `standalone` chain. +* please check exact instructions provided by the `standalone` chain team +::: + +After the `genesis.json` file has been made available, the process is equivalent to a normal on-chain upgrade. The standalone validator set will sign the next couple of blocks before transferring control to `provider` validator set. + +The standalone validator set can still be slashed for any infractions if evidence is submitted within the `unboding_period`. + +#### Notes + +The changeover procedure may be updated in the future to create a seamless way of providing the validator set information to the standalone chain. + +## Onboarding Checklist + +This onboarding checklist is slightly different from the one under [Onboarding](./onboarding.md) + +Additionally, you can check the [testnet repo](https://github.com/cosmos/testnets/blob/master/replicated-security/CONSUMER_LAUNCH_GUIDE.md) for a comprehensive guide on preparing and launching consumer chains. + +## 1. Complete testing & integration + +- [ ] test integration with gaia +- [ ] test your protocol with supported relayer versions (minimum hermes 1.4.1) +- [ ] test the changeover procedure +- [ ] reach out to the ICS team if you are facing issues + +## 2. Create an Onboarding Repository + +To help validators and other node runners onboard onto your chain, please prepare a repository with information on how to run your chain. + +This should include (at minimum): + +- [ ] genesis.json with CCV data (after spawn time passes) +- [ ] information about relevant seed/peer nodes you are running +- [ ] relayer information (compatible versions) +- [ ] copy of your governance proposal (as JSON) +- [ ] a script showing how to start your chain and connect to peers (optional) +- [ ] take feedback from other developers, validators and community regarding your onboarding repo and make improvements where applicable + +Example of such a repository can be found [here](https://github.com/hyphacoop/ics-testnets/tree/main/game-of-chains-2022/sputnik). + +## 3. Submit a ConsumerChainAddition Governance Proposal to the provider + +Before you submit a `ConsumerChainAddition` proposal, please provide a `spawn_time` that is **before** the the `upgrade_height` of the upgrade that will introduce the `ccv module` to your chain. +:::danger +If the `spawn_time` happens after your `upgrade_height` the provider will not be able to communicate the new validator set to be used after the changeover. +::: + +Additionally, reach out to the community via the [forum](https://forum.cosmos.network/) to formalize your intention to become an ICS consumer, gather community support and accept feedback from the community, validators and developers. + +- [ ] determine your chain's spawn time +- [ ] determine consumer chain parameters to be put in the proposal +- [ ] take note to include a link to your onboarding repository + +Example of a consumer chain addition proposal. + +```js +// ConsumerAdditionProposal is a governance proposal on the provider chain to spawn a new consumer chain or add a standalone chain. +// If it passes, then all validators on the provider chain are expected to validate the consumer chain at spawn time. +// It is recommended that spawn time occurs after the proposal end time and that it is scheduled to happen before the standalone chain upgrade +// that sill introduce the ccv module. +{ + // Title of the proposal + "title": "Changeover Standalone chain", + // Description of the proposal + // format the text as a .md file and include the file in your onboarding repository + "description": ".md description of your chain and all other relevant information", + // Proposed chain-id of the new consumer chain. + // Must be unique from all other consumer chain ids of the executing provider chain. + "chain_id": "standalone-1", + // Initial height of new consumer chain. + // For a completely new chain, this will be {0,1}. + "initial_height" : { + // must correspond to current revision number of standalone chain + // e.g. standalone-1 => "revision_number": 1 + "revision_number": 1, + + // must correspond to a height that is at least 1 block after the upgrade + // that will add the `consumer` module to the standalone chain + // e.g. "upgrade_height": 100 => "revision_height": 101 + "revision_number": 1, + }, + // Hash of the consumer chain genesis state without the consumer CCV module genesis params. + // => not relevant for changeover procedure + "genesis_hash": "d86d756e10118e66e6805e9cc476949da2e750098fcc7634fd0cc77f57a0b2b0", + // Hash of the consumer chain binary that should be run by validators on standalone chain upgrade + // => not relevant for changeover procedure as it may become stale + "binary_hash": "376cdbd3a222a3d5c730c9637454cd4dd925e2f9e2e0d0f3702fc922928583f1", + // Time on the provider chain at which the consumer chain genesis is finalized and all validators + // will be responsible for starting their consumer chain validator node. + "spawn_time": "2023-02-28T20:40:00.000000Z", + // Unbonding period for the consumer chain. + // It should should be smaller than that of the provider. + "unbonding_period": 86400000000000, + // Timeout period for CCV related IBC packets. + // Packets are considered timed-out after this interval elapses. + "ccv_timeout_period": 259200000000000, + // IBC transfer packets will timeout after this interval elapses. + "transfer_timeout_period": 1800000000000, + // The fraction of tokens allocated to the consumer redistribution address during distribution events. + // The fraction is a string representing a decimal number. For example "0.75" would represent 75%. + // The reward amount distributed to the provider is calculated as: 1 - consumer_redistribution_fraction. + "consumer_redistribution_fraction": "0.75", + // BlocksPerDistributionTransmission is the number of blocks between IBC token transfers from the consumer chain to the provider chain. + // eg. send rewards to the provider every 1000 blocks + "blocks_per_distribution_transmission": 1000, + // The number of historical info entries to persist in store. + // This param is a part of the cosmos sdk staking module. In the case of + // a ccv enabled consumer chain, the ccv module acts as the staking module. + "historical_entries": 10000, + // The ID of a token transfer channel used for the Reward Distribution + // sub-protocol. If DistributionTransmissionChannel == "", a new transfer + // channel is created on top of the same connection as the CCV channel. + // Note that transfer_channel_id is the ID of the channel end on the consumer chain. + // it is most relevant for chains performing a standalone to consumer changeover + // in order to maintan the existing ibc transfer channel + "distribution_transmission_channel": "channel-123" // NOTE: use existing transfer channel if available +} +``` + +## 3. Submit an Upgrade Proposal & Prepare for Changeover + +This proposal should add the ccv `consumer` module to your chain. + +- [ ] proposal `upgrade_height` must happen after `spawn_time` in the `ConsumerAdditionProposal` +- [ ] advise validators about the exact procedure for your chain and point them to your onboarding repository + + +## 4. Upgrade time 🚀 + +- [ ] after `spawn_time`, request `ConsumerGenesis` from the `provider` and place it in `/.sovereign/config/genesis.json` +- [ ] upgrade the binary to the one listed in your `UpgradeProposal` + +The chain starts after at least 66.67% of standalone voting power comes online. The consumer chain is considered interchain secured once the "old" validator set signs a couple of blocks and transfers control to the `provider` validator set. + +- [ ] provide a repo with onboarding instructions for validators (it should already be listed in the proposal) +- [ ] genesis.json after `spawn_time` obtained from `provider` (MUST contain the initial validator set) +- [ ] maintenance & emergency contact info (relevant discord, telegram, slack or other communication channels) diff --git a/docs/docs/consumer-development/consumer-chain-upgrade-procedure.md b/docs/docs/consumer-development/consumer-chain-upgrade-procedure.md deleted file mode 100644 index 4af18552a3..0000000000 --- a/docs/docs/consumer-development/consumer-chain-upgrade-procedure.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Upgrading Consumer Chains \ No newline at end of file diff --git a/docs/docs/consumer-development/offboarding.md b/docs/docs/consumer-development/offboarding.md index bbd4cbf35e..7f2ebb0f5e 100644 --- a/docs/docs/consumer-development/offboarding.md +++ b/docs/docs/consumer-development/offboarding.md @@ -1,5 +1,5 @@ --- -sidebar_position: 5 +sidebar_position: 4 title: Offboarding Checklist --- # Consumer Offboarding diff --git a/docs/docs/consumer-development/onboarding.md b/docs/docs/consumer-development/onboarding.md index dc870b76de..c9b503ed66 100644 --- a/docs/docs/consumer-development/onboarding.md +++ b/docs/docs/consumer-development/onboarding.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 3 title: Onboarding Checklist --- # Consumer Onboarding Checklist diff --git a/docs/docs/frequently-asked-questions.md b/docs/docs/frequently-asked-questions.md index 6cbf364a36..e431ef3f01 100644 --- a/docs/docs/frequently-asked-questions.md +++ b/docs/docs/frequently-asked-questions.md @@ -8,7 +8,7 @@ slug: /faq VSR simply means that the same validator set is used to secure both the provider and consumer chains. VSR is ensured through ICS protocol which keeps consumers up to date with the validator set of the provider. -## What even is a consumer chain? +## What is a consumer chain? Consumer chain is blockchain operated by the same validator operators as the provider chain. The ICS protocol ensures the validator set replication properties (informs consumer chain about the current state of the validator set on the provider) diff --git a/docs/docs/introduction/terminology.md b/docs/docs/introduction/terminology.md index 59994353cf..fd06174f92 100644 --- a/docs/docs/introduction/terminology.md +++ b/docs/docs/introduction/terminology.md @@ -21,3 +21,18 @@ A particular protocol/implementation of Interchain Security that fully replicate ## Mesh security A protocol built on IBC that allows delegators on a cosmos chain to re-delegate their stake to validators in another chain's own validator set, using the original chain's token (which remains bonded on the original chain). For a deeper exploration of mesh security, see [Replicated vs. Mesh Security on the Informal Blog](https://informal.systems/blog/replicated-vs-mesh-security). + +## Consumer Chain + +Chain that is secured by the validator set of the provider, instead of its own. +Replicated security allows the provider chain validator set to validate blocks on the consumer chain. + +## Standalone Chain + +Chain that is secured by its own validator set. This chain does not participate in replicated security. + +Standalone chains may sometimes be called "sovereign" - the terms are synonymous. + +## Changeover Procedure + +Chains that were not initially launched as consumers of replicated security can still participate in the protocol and leverage the economic security of the provider chain. The process where a standalone chain transitions to being a replicated consumer chain is called the **changeover procedure** and is part of the interchain security protocol. After the changeover, the new consumer chain will retain all existing state, including the IBC clients, connections and channels already established by the chain. diff --git a/docs/docs/validators/changeover-procedure.md b/docs/docs/validators/changeover-procedure.md new file mode 100644 index 0000000000..4149d9b412 --- /dev/null +++ b/docs/docs/validators/changeover-procedure.md @@ -0,0 +1,88 @@ +--- +sidebar_position: 4 +--- + +# Validator instructions for Changeover Procedure + +More details available in [Changeover Procedure documentation](../consumer-development/changeover-procedure.md). + +A major difference betwen launching a new consumer chain vs. onboarding a standalone chain to ICS is that there is no consumer genesis available for the standalone chain. Since a standalone chain already exists, its state must be preserved once it transitions to being a consumer chain. + +## Timeline + +Upgrading standalone chains can be best visualised using a timeline, such as the one available [Excalidraw graphic by Stride](https://app.excalidraw.com/l/9UFOCMAZLAI/5EVLj0WJcwt). + +There is some flexibility with regards to how the changeover procedure is executed, so please make sure to follow the guides provided by the team doing the changeover. + +![Standalone to consumer transition timeline](../../figures/ics_changeover_timeline_stride.png?raw=true) + +### 1. `ConsumerAdditionProposal` on provider chain + +This step will add the standalone chain to the list of consumer chains secured by the provider. +This step dictates the `spawn_time`. After `spawn_time` the CCV state (initial validator set of the provider) will be available to the consumer. + +To obtain it from the provider use: +```bash +gaiad q provider consumer-genesis stride-1 -o json > ccv-state.json +jq -s '.[0].app_state.ccvconsumer = .[1] | .[0]' genesis.json ccv-state.json > ccv.json +``` + +### 2. `SoftwareUpgradeProposal` on the standalone/consumer chain + +This upgrade proposal will introduce ICS to the standalone chain, making it a consumer. + +### 3. Assigning a consumer key + +After `spawn_time`, make sure to assign a consumer key if you intend to use one. + +Instructions are available [here](../features/key-assignment.md) + +### 4. Perform the software ugprade on standalone chain + +Please use instructions provided by the standalone chain team and make sure to reach out if you are facing issues. +The upgrade preparation depends on your setup, so please make sure you prepare ahead of time. + +:::danger +The `ccv.json` from step 1. must be made available on the machine running the standalone/consumer chain at standalone chain `upgrade_height`. This file contains the initial validator set and parameters required for normal ICS operation. + +Usually, the file is placed in `$NODE_HOME/config` but this is not a strict requirement. The exact details are available in the upgrade code of the standalone/consumer chain. +::: + +**Performing this upgrade will transition the standalone chain to be a consumer chain.** + +After 3 blocks, the standalone chain will stop using the "old" validator set and begin using the `provider` validator set. + +## FAQ + +### Can I reuse the same validator key for the `consumer` chain that I am already using on the `standalone` chain? Will I need to perform a `AssignConsumerKey` tx with this key before spawn time? + +Validators must either assign a key or use the same key as on the `provider`. + +If you are validating both the `standalone` and the `provider`, you **can** use your current `standalone` key with some caveats: +* you must submit an `AssignConsumerKey` tx with your current `standalone` validator key +* it is best to submit `AssignConsumerKey` tx before `spawn_time` +* if you do not submit the Tx, it is assumed that you will be re-using your `provider` key to validate the `standalone/consumer` chain + +### Can I continue using the same node that was validating the `standalone` chain? + +Yes. + +Please assign your consensus key as stated aboce. + +### Can I set up a new node to validate the `standalone/consumer` chain after it transitions to replicated security? + +Yes. + +If you are planning to do this please make sure that the node is synced with `standalone` network and to submit `AssignConsumerKey` tx before `spawn_time`. + + +### What happens to the `standalone` validator set after it after it transitions to replicated security? + +The `standalone` chain validators will stop being validators after the first 3 blocks are created while using replicated security. The `standalone` validators will become **governors** and still can receive delegations if the `consumer` chain is using the `consumer-democracy` module. + +**Governors DO NOT VALIDATE BLOCKS**. + +Instead, they can participate in the governance process and take on other chain-specific roles. + +## Credits +Thank you Stride team for providing detailed instructions about the changeover procedure. diff --git a/docs/docs/validators/joining-neutron.md b/docs/docs/validators/joining-neutron.md new file mode 100644 index 0000000000..a9adbb25f9 --- /dev/null +++ b/docs/docs/validators/joining-neutron.md @@ -0,0 +1,15 @@ +--- +sidebar_position: 5 +--- + +# Joining Neutron + +Neutron is the first consumer chain to implement ICS. + +You can find instructions on joining the mainnet [here](https://docs.neutron.org/neutron/consumer-chain-launch). + + +To join Neutron chain on the replicated security testnet check [here](https://github.com/cosmos/testnets/tree/master/replicated-security/pion-1) + +## Resources +* [Neutron docs](https://docs.neutron.org) diff --git a/docs/docs/validators/joining-stride.md b/docs/docs/validators/joining-stride.md new file mode 100644 index 0000000000..96e32d8fa2 --- /dev/null +++ b/docs/docs/validators/joining-stride.md @@ -0,0 +1,20 @@ +--- +sidebar_position: 6 +--- +# Joining Stride + +Stride is the first consumer chain to perform the standalone to consumer changeover procedure and transition from a standalone validator set to using `cosmoshub-4` validator set. + +`stride-1` network (mainnet) will perform a software upgrade and at height `4616678` that will transition the network to using the Cosmos Hub's (`cosmoshub-4`) validator set. + + You can find instructions about the Stride consumer chain launch and joining the mainnet [here](https://github.com/Stride-Labs/mainnet/tree/main/ics-instructions). + + This [Excalidraw graphic](https://app.excalidraw.com/l/9UFOCMAZLAI/5EVLj0WJcwt) explains the timeline of Stride's changeover procedure. + +## Note +Stride re-uses an existing `transfer` channel to send consumer rewards to the provider chain, in order to preserve existing transfer IBC denom between `stride-1` and `cosmoshub-4`. + +## Resources +* [Stride docs](https://docs.stride.zone/docs) +* [Changeover procedure timeline](https://app.excalidraw.com/l/9UFOCMAZLAI/5EVLj0WJcwt) +* [Changeover upgrade docs](https://github.com/Stride-Labs/mainnet/tree/main/ics-instructions) diff --git a/docs/docs/validators/joining-testnet.md b/docs/docs/validators/joining-testnet.md index 5ae9007416..dcf654786d 100644 --- a/docs/docs/validators/joining-testnet.md +++ b/docs/docs/validators/joining-testnet.md @@ -178,9 +178,3 @@ gaiad tx provider assign-consensus-key consumer-1 '' --from Lfo_lmY+ zVwK^lm3Zf?P>%(^Y}YxVe3F8~Y8M5?o%<9N(|GGnHwDGby%ZEbE>ch&@};1lyAhC~ zB#LjW)RvQ$q*x&Td!8NXiFYW?WlkQa?4qQlT1y$fW8yR3T4HRfaC%93$gOAiO%yVc zM^0PT^)*@t_~^__3{9Lqbh4OH_R?iqrYBy4j``ZF*(2F6C$H~Fx*E@&*<7M8bkwxY z)Zv=YrrHEn&M_*^OVTgSgjOCAS+6Vq=;hR0YsH+eyWjdBD#0~fBeA_11HCx|6E{E2 zy(|jV*YrGd;lc$*T*5D}$1A=0HGX-QydkkIpLfYWeCGtyC7*wIn;r+vPt1_4W!~5F}?`tgmhxZ=~{+C|91WmXf@9#HsXTyIC z#q8zFx*31m?9u;I6%;$ZwYq5JD*e|y^CTztC-eW{;cpih|KQ*fy668S zi+?TVNv;1e>;J-u{%ihz|HFUVI{p9P@+D{!^~&80VzUKTQZoniC{M2D+_T3;%{cpj zQCjQ6-JG1o<_Gbyv&(;B8+U^5yg2r#klja}RzR=pqdDjGWA36qPq;81;F=!qXL8=c zQ(}H_R?>wq#!YNeE)FK`g}NDA@$2lC{vs&7UH>X9Y$vl-_lvao>E8BscZsH! ztchlO38S2;PRpqdpHSj#u$uic{qdopN6^|S_LVo|3@Wf@@Gnyv@58X^0 znCkR5)*kJyP*PXVI$pkqm$#|S8hg8D7{KJ;2k_3euM!JWMr7`2C^QCtWbu(-%PO-DI%Vb7HT}>SD+pn*z zZaem^0>j8Mn?=JWRZwm}KQcL0ZqFPl?OrxIPCk3(tIK~%N`GC#b8RX*UimtVQ(a5e znb)f9{d42>d|z}E*LkmFS5Lcf>5W<{?^9k4M!BbZ9Qkr*`Cq9WO;H(6V5 z_tbQ7q+EhUBg4FSbW6<=@25|9^{=4XBB`&hZ-e2Aw;XOemva5Qq~zBa4Ur$;Uej6k zC!dv+ysWExT(ZD-pHACA=AawiHy$66a%J>Rip`n+@!``a*UnG=deii{%oA+Ch>&sj z4*iGpvPgRTtlQ;L-vQgEV-`-qE<`SRjOAM2+ogTef6W6-3D|fuk;E9iq z&rfFC+2JCS4?7-R9~Y>myb@C#F6ZMbY@+=&T21In5k~d!vtQTbw$m0C<5xb-O!vlm zju#&NF*$rL@ybcjpC8ziPMz9N`jp4nU3_}?7Cz0{dsVx6cwFDSc@uRZ`5J%9#k{41 zAEfqZW(ntD8m@kgQi_nPy%J=cNr>DQ#)ZzT^W)T!iP-aryI5H*>hvQo_l4s3 z9l7L_+GJ(Wm%A`;(ri0%TuDi3)8@_Z>y2}_Z98QCVRRUS`FPhaQt3rXD8|o{UY~iL zl6PD89j}?08HQ1R$bMnMexG^w=TAYw{{Bq4lkfRLWIT6OL@9+{ZOXcR`qgPpR@Q4@ zqn>bL?j1o z=2k7}t4(Y#<;!*H>+6d;8>_khdd*(;%Re7kwLYM0du9+*+c!m4Sc+l2@b|~ZLX<+K zDw6cS)L0K@v$2k4Ur4xIO#LbptHq>N*XR9lI*m< zx_`4_eTr*Uv|3TQ-FTwcZFh-z%St;8q#%RE`E%#!#jHjh90rf_DGImV_oDbP*7?zI zBPnQ#5_9{>wv=YEHQ#DdK*O{yVw>=di=(~Ou??0M78dHJ#q1T?))P^h*;bO0E=)qa zQtrKdLP@vctVxyYqv}{Z~ySxJfx1}~(sO>kX+B4pGtNBAo$*UWhwG-ERD#O)A zlV7SvU)XQlWRuv%rN!nXp2)?Hp9m&n@;U*%DCC9 zU6-UUgKav|++ro?!|Up!Wp{83&xK8|&&IlGSk$twe%N4~J13YK0oHkE_5iF)ccznxK=J5W=Z{nR@+n8i3}>dKz;2|KyY z#%z_CsV!&a;NWl*8Az+SS*^jH)pa&^`pn0Gg91Il=9MxPS+_^%CG2c|jv0N4I9U=T z6n5p)Ga2jmPMg(icS&rSnwm1HH%xv1^`(kR;iL827-?vZJzVQJ-e1qr{{H=Y8Eb4C zfAs~d9@#o9@AxkYBH`*<8Rq(HQK)V=S?LtIVDFIIiAnE#X4)%0UZb;lrG}Iw*~Qrt zO-y`QLr2$c72eAzc%9xy(@Nho8=cJL$IHm4na1B!LRFdOa9@2!_Dly+s@T&-zsXxc

PhlGT5H|N@uVmSO&Me#W4(BE!+IVr^cn^kl3yFt5&%Ql5%sA0pxbagIQR#TcuyJb(fPss`!DM@v^ zsScBt-zcTRWxYdgk90o4Vy2- zoz=Xp``}WDgy6DUK%+`$S+IlzerD5y2M?HR>=x$dLearZ#%uLBbXaVLj*Jd8T6@m;i`+5_ zu=!bRGB;Ko>Eg5Q(H;ZrUd|&vu1rF1xeIfH#d?Ni0S7nR2?|B@Stpxzl$cf*xou-= zFLaeFvpN>+IcSYK;1O4@7j%0UhI;cx4h8wUylgo$Yc;XEhkv||-4*QWG%jj2+U<+a z`uge&cU`J63$TdOt23|XpWDJYi_`sw$$UkMq0hSbJ?KMZc&oMS1MlY_m)vSmbE%^W z(95xTVSZvCO1-Oi?_|5X`^;dj1lc0%%FZWTmU+yXxqK?VhKr{fo%wQnDmk0<-kNx=OX<=ewQaO*C zZ!6)Bbt(4b;LyR+?p(P{*ggu~y^zgalop8MnhlCw{LDb6bhg!)HjvveE>#+ZnN(x6 zdAhUeCdd1dz9^{8eROc(REhAS$`Duk9cnV^11<~# zyL4^(YU1}@`RGe-Tgbp3AmaMAUlG|dE-W9w5AGg`(a*>B*-Q>$6%MQ%oU*&vR^rXw z?!+B)Vs(Z{SAba?hS~YyIwmPhMcWf;W=HH6f>lUi9v-hZmPWmQ&`ZrMnwCH8Z4R9#mJUJ)w1=F`BioEisNcG z?PzG`W+oI@iw1y`tn4Cmq9Vt3CfcquLANvntxrZmL_{PA_26o4g07?_SpuxAtFvry zfgb5Me|&Xx=FQm6Y}xXggM%gdCAUqN`21u$J(_yuU}NU3pEu8^8flP<$NAe6+nI4! zD!#0}Jwn&M?nd=&2CzmC3QbDdpOBCcf|dG^nkDFC@8=iCN^HsRUcc>7L9E@_YkGkz z^74NC?DKPj_8f!rM<}v%3CIXE~3&-LjQ2c(;I6{5`tzge&aXD}_%Y_b{o zls4JsEF^4S875PF8;G`RaYL%AR$b|bvvh|gLOFu!FIw`)A4)RO4_PH7Bzow!Y%$v3 z&CjYGn>{Yu(`;5QhT>^IQ{UVcYd>d@WLPf<#2#Ct9~s!zpW3VtLx-mMc*ilpw0SR1 zl@iy+8}HT;2qcUx+wRB_bmF{$?e>AJc4rdYKw+P}q`0 ztFa$vTA8^4g|>-UxL{wtU(FHl*wk$^1Dc`ioeq|T$)YIbaM_Z8gu!o}K%5sUH9@^L z(a}k#8Z~mQ&2X9I^-TyVKOfnHWB?wl(k;Q)2B>jTop`I|ZMJsb>f0nkp3N<;P$4E;S z$nj-w0;zH+msqd~wGKkVUCS&g+@p1?@g|x6=tDEnwY}gyD*5YKY)30Tl$Oe!@14XMT1{32~pX-fDN-@mdb7n9#H|;Wca4gn%mI z_iRnR_O0%f-f>AAj6Pv=Uhy2kHvFXWtZOm@CX5{QKVYy0aFnbIf z4jukE_NlCl^m?}R6fD>@Q~;H#((-b}gVlo~gIOaU%e!=xNQ%q<#L$ zx7TMQ?;E6C`^M!aF+XWKQj&9)DG_}(NL>6$uYYGHwEDi>5tA)~C}!nB?U{}6-f7}AiiE}ieK zKeU@n0-dJir3*lmtmw^8lb`~qGzB`1_I!zK=z1gT&1HfbbG+b|)|aked+~HZQa*s< zD+lo}+3g*#V{@L}KO`jy#B3Q_K8RK$yP*?8jQvnh=|FtQ#TsBQzRvRC;L(NANQpCP zWeOtwUX=@WlO5pgZN55gj_;@?M9;?p`a&@By0y^yh{cVNW&j`M`M%q|(W#5etkMo^ zMKHr11@Pc)3GOqmylh-qbEsAa3K?sD@9N^vp;NzcWEKU0i_V;ZhQJUnyV}PWu>dNi ze+g0b-6<2BXz-eMpkX5|hP2ymEPsO56bQpUsQZq-^x*)u(5r^&Wh>V@K!`Xq*_U^Z zvIqi(N?{e{&Zh@V+6qjUcpbR*^+ZoBWTdA&YN`WG_H(^joR?6#3H_);K?ftf+G=&H z>9$^iDi#o--RxgSh}3cUZEI)Evur!S9GP6*(x;r&XJgbE(BlKW`0?ZWAXR-gnEWKvd^)WP@(&|6t?cw7&9y_JN}1l8nu^-Lv!9gl z-q>7b0&&xCg@6~5Vd<_*Hh9oG`{Qh`Y#ph;=rMc%c{GGq^4p;~M}Bm6ARMSN2f#=h zWXS{&Ix_1W4b91C`yH|Q1y-)#>PuUAPB6>7qr}@kxhvs_E4BFKMcTa@9gck%f%jEV{T&OHKsdKu+Qx)gCM$`eyz&WF^#gVI#BL| zgV~1i+hiTu&Y0z z)p!}@dKdI_^4|ngxDk*wkTWyz-D+mMUk7u@X%J{q$Us0V#~feIWQe*eCdLGV$MM2B zJUo1H#6F$}6nN13glH2?###GThjDXp$)?}3t)r*=Y0?v0Axd687`6!k}2C z59Yj+>y0wm`vJcqmQQWT3?b z^MQtg=}oA%-5AmI8S=AXjTkqgIGOiUvhPw1+B%rKU`J$f@!6qwgtfPUeeg`&=AH55 z z<2uIuCm8%OkYv9TI)&{|NeW|pv)#=5=+vfX31vi#@G9Sf^3n6F(PnX2cNtyj!*h08 zbJJs<7PW`dHYy13&@YD^K$tgKpaD=l_UTuKeZPRY)){KaGwFWus1UH!gUBGHg6lxy zB^|ukX5{39@?bG*i=?X+tfU?jI1NE>fBJZ-);vG(sLG|?nvw;8IZW8_JK>E0sS|mHK;6;k0b0M~UOEDyqqV7e+Q1ozrR7l#jk6OWj! zqSQZMmn=XD@*MwO?5Ps-?WGC_qmYq$t8H&IRPFGEp2%RMgepSCR?w+G<`=Z+tMQev zw@Xi#-gfh=RCOIVvBx;6YBV&xRng8**rW@GTHkpZ5Cv3HvSeoKaAW4}YK>doR1qJ6 zD0ww9cFqYkS%M*6t9ru#xKnJ2K@0&T??|*omnFJD$v4onN4xsRmL%vdvw^a*M+{VU z5wJm~J!j+!wR&+stElUKrj)7>vdD*u;;84B7{YhWiuvKo$p6kJ;^GV8OX}XQE@uAwe zN3GO~;8?+fUkqOWkaFfZg5SEgwYBkR#-e=?+sP{)b(H>~>E%A?f18`Wz$gftgX~`b z)zt2@09Zvpr4|g%$?D_! zf_pl|YJ9bz7c#A*CDYdXPtyjqHUzzwNQPkg!9v1=pN~QZYEK-A$xqMQKMz$MIls+cvCZHxLeQ z>v19~yn96LF2|c5){qSH^uBq1Jhi(zR_pXjWp)PR&(Dt#*D~YQAainii4AnAU0Zqm)$gqJ9&2DLyy)#Z^2PLeyfj0BCD_ zd~Cf;`uN+&D&nJD^c=#htP-`xM}S zJr)q-9UKUoAS7_n((QPv)|Q$(XNcX%LzpMQE3))@_z|IA8{&}ipGV(Dr07478PxFW z6uF%|IzHdLFk>GNo!r}>uw}yFK>^;wJlqHfSo1xD*c#9e3&?7O&gcxt_qlkwPtK*5nBCh1<&Y?og7g;W)YZ3Q3CRLR_=sC_SZ(Bj3(yz4u_3BfEZTmjUSrqSM`>CY3TAb5To|fSrcmwx0wC2fK89 z|6V|ZcN91$!ZfkDHWD#cK3H6Am%Q%=m{^LPO_uGhoWFUiwGr3~eIndN!|W3fvLi>K z-Aq3n1X(x8{qKp~ZHbE4_MT2lekr><%-pmeuU@zPM#=0TP$tZF$f7Up{Y|I}kv7qg z0p5L@ZqS0ay${m1SA14h7SWN~7s11NA3&Tp=M#xDNouNLLIjGB2xxFSG-mliANba1 zUY(X%9WuLPrXj%gxFp&l5mP--UG3mVTRWG-W4_~BKWpEC`Tw_QN>&l!-OopGh1`3ATrg*x;&GAMe9)jZb3iScYHW%+?Qm8)ZVC9o~!N-SuA z8+cYXrP$!&P%q;&_!>onv2`UKo@^Upbhdr>RCL1}THl-KODN@yFXhd=uDTAI4Y8FB z?|o@Vd3E|FEg{3|Rz2bS-~u~15C9EWX_7bq_VGit8ZwuVBt&u9$sbq(bns}JcMV3P z*vz)kNsgJ1QMa_EKU|Is*JoT44mV!^#?B}R>fcy zk3x59utb|ou?+UljD-z(pxU4|A^bT`0^Ko`|Ak!5I-H5VaA10@m+niX;=++UV~Hz} zorWQb@0uex=?lBzEihmcT<_%!mu(6tbvgwOG^kd$3Xr~%xGCT_p8*btMs3p2TdIg)lm_P`_*e2P?WwZ)}Nmxve-s(V}v zJ0kVn%fU%Pr<0)4yp{KX<2~fb@r(Of6NbCl)sN0L_uRVo+w0W#6RO`q0HiC$+g$`K zqOud(iT$n))k?-5)k-Pdw4;A~7Dlxcb`H_0X=vgxh(we_uQi-*7=z+S0|j8-qH)CS z&7zGCkKB)!5(JBb&o;N}KR3~2aUIs7=Q#M;_`LxhsOD(R(XVu%sr|06ekd+>WOEbN zepJUT5+naeT=msyeSb>0o0`oU_yxaRPr#Jefxx-J;&dGsjb7VUoiVx&>{pGFFSbnHrd z#25r;n%=D!*`u$B`nWoKMK z(mk4kHhgay8WOdT_ksF3Byr^L+iQX8&&=~%_vV+8LC=zcE!!MF<-}qebLM#=3^2My zYylhpM=e!G8$q!T}_DFUdd=r;wl+d7tGXF0e7VK)+z3-lL>es9Pu{ zxQw3v@(Dt39l2xDj^5xw2*cvnLxGbOe`Jj0eaQBFD|Nc$KVzm8Zxz1)dTfT4>ZS#R zMl_8`-IGTaHFpv5U;Mtef{UfA?Y*-OTl8bw}xXUl}e(f=}zg#>n=HC`GmV+bbnM z;GmhuI5?Al#(EJ41as)YBqb%YATd=ya>C%@r^EbkWc6YuC+utz>LJ!B`jR`jCTPW3 zc~e*vE+l^l>D0fcl3dYWuRA=C0fCZ;W+m_QZhae2)5$ut<;AjxR9%8BQb7V5%PEGw zU{)Eip%h4QCyQn`7uR&20m;)?jrTDSb{diT@0VYHQbtB*(VhlA)XVVV`H8k8Nrd1+ zVi^Nkl$h%?sKCKRM$o>E?7axN;wX4{JC8(>x3od#O3Y<&wTGY04bD?-polxkqn@^x z$eF@GbD?HkWh7`}sPtJ^l2}1>geACaJ;?v1{@-Bko&Gk&J{-{UcM`phxI+=8wXCRW zCFZL|0>G{IpkQo4B`Y=OWEjC^?mXi2$zO=h95sqULg@@SY*Al^iD>s!6n7+29ND>O z@_xKM(t2ui-76m}s^fTU@_S!8!Il%fmG&&@~!7ISCm;b-@eNEdQpLJ zhD2#3Mt#lgu0YSnd8ADOT@ZuDujz-NCxnySsZYip^Qflnq|1`vf<%w4vE$vlcS+%G zgI%SVW0Jx{0zD}6tbBZYU(`TK|Mj|Kcn!kIi5}vvu1EhT>?K03{yNzwvbliNe!QIR zuTD?#c3wg)i2uV?iIA%sAkIlxBOu40Hwl1au^nvL5mb)krc~!tUl9q!4%)O26CZFR z_@5MLB*{QznfeWbs@DnZ;cmM?j`DS_o40qHzq);@5PcFj_sGUlV|y z9-jk(c{5Y&Rz=A1{{Uo@#UjWegrKLHl#5LiV2@+ci5IfXA{`%2K3HlZ4lT`Vp5Uqq zD~sij5Kq=oZP`O~C(>!~V`oTmA*>k^#P4O`IvW|54bVU(+CZq}rwO0YaXYw+H={cw z>U?77l~@5N`cPncrMg^@q2M_HrJ$M0uG~6V^szwveTe zxkGyPBb>lOcNP{TUyR5qRDx~;;mV}yyxwtS;{=o!Cf@N1onBb*U_5YbXFpoTmHmCD z2&3(@9`Bn3uw+BT0HB?29h0C-AY!8aVqgq@#pRL2cfD2}6Bs{7wnP2XIJrA(XAoCz zLwwO0l5V(*A_B5lN?Xur+@P|FlP2UElL+W$36HuRrbEMk^(0~gEQmPX*souCIa6Y0 zMjX|~5^IKNnrc}jI3aXU59yHwl}kR*#GC*!ioPgHWS{R!m?OxNHHHL)i;HUqlHnH|3b%)`X?(+o6w#qP%J}QG z2?VV+TGaCp8V$X%35GZ%SyYXXMV-=}VTee6cX{Li6Ro`(NYOWiJ${_B+pU}K0 z=Yb5x!JBRLNS;b0ZR)nux5m(8Hrr>HC9XFi|)VmyUUUoO%l1> zXE!@}vdj>7OZaI0o2TGD1o(XP7ve*0b0-GCls;8hezlV&nz=w@PN2s*JqKLwi+Tbz!Q6Gu7cI>y>s~2^_(HpSz z2%z#B9y3_Wz`qk}Q1H`{S1?s7HQ|b=Mw6%<;4g>v+dE5$cI2Xn7QxC&Tp~*>h|&kP zf95D{CPu=skBlLQQti9Z@|K|dl25nEf`-NI0KJ7n2fCCKmh|2qNOA!in1h3=rp-eE z^h;7py87g)068FSA&(-$ABMg`5(`8%iI9_V#@{ajD&2BczS)QvBS70-^>VJuNPl6}7S)6X@_zuAk_b#BZvc8tY$B_3p!B7emh%mq8 zy_M_uHJI_)haVu~p$*%AA55Qf*;Y46(2qbPuZ6I+f9|EYa46@eFMZZUl!{2KWSlem zQ`E@M648l>7S`)X>XxKs_gN0Lm{wOkI`(v3rYNL{PzYO`Bcz|CK`+xDZhOyUQ${j? zlhBZviKs=%Y`L8j5cOV?%Q`g$vlbxv0EzM|9OYtTCAnmwW*ak65aG=+5|{GtPku7C zORUI7r#XO{7W_2Wpnz`SM4?BOHb~*Kw2$8pAeZy>H8Y*45P1R7cJ!9(~;s$z_k= z+-(Q0oo)aYgLVUk=e7N_g9DNYQjrRQ+VJ`pEntGyiBwA*eyvCxBalL8*TIJ8?D8jd z7ghoCC)*Kf5B#c$Hc1dT5m7qMv)D~usw51XlF=u6Nai04+9Tl+aoY@28@G^baqj$BEQxdNtZoJ> zP72VdjVm3~QwCj$46?c=`?+I!&Q2nLpS2Aj%@MKwU2~8V2mK<5zofsI}OVKqAPuE`hh24zjYs`P@jNR>-I9z_!@{9Ay!3BY!m!tH?o|#gJ?^ z2_@iic}R4g)PgnuRTm5kM>Y4Fqk)JCREl@{>QtHrVff#=$=X?nvz&A@Re)0>_Q* zkSQHVI$)|_pN2-7`SxY9@b_J>i6d)-l&yWJfy7_dhHdp}v?6|4NqITn#7QAs%^mr4 zfDj$jO#c8OvQ&t=jgC`nUX1T9zTUcj0D|?fz~&(i5#()^PO@MsfYFHE)P@q)u2@YB6-b}JQR?Ul-;Xca7)6TbR>?vrwFQC`{qQ_ zkEA+(1UZnmi+O_v=T{F$D=ZM%ZBGtGH#k>28LW{mQ4YE_m^~3OS}xq~LXh|yATh+Q z{pt5#J>^2bk9Dz@CIx6=F`(B-AzVn>5CluD)>&8$<>268C$a*}r-AL8}&DLEjb(iw2G>U=zaKFO)0)b1e( zCvphF)D}mecv1%d-Fz2C(xiaD%6?p+Eg}yDEcEcF_$4g74KgC=j<{Ro;FPYFzau$$ z2v(1|zv7QIw)Iwni3u*c2)V12-bB9i?;fObG?lw7jV_3b61;^2-9BgkF=??7!J zhol)vYfnScL3!zpFGNn0sUydU{(uC^og|n6`vnd;IF{E10?HPdE{S2_h?dFnqd*RA zIF%(cO(xKR1QxRz8sOAgB0o(mII^W(_;C73pCIYQh~Nx~2MTw1sgQh!$>$@Ki)jc` zKm$d^(kZYk9{ut^Vu}HBC^V$alf~|3-Gta$SR>}h)LU@NDgX%ats=s7)fBihwsjU9_tXz^$Z*u4u72}4P$hMcLxD)GE6a0Tws z;8GDS^t;PhxC16zRlrZ8eahGaK&YJ_!AAARKYgKa`id6G~%&4JpsELc&DX-k%9`ad=l2s->E_7IuF-gza+ksl`o1 zrfEGFY;Yj=;L(wh5$=GqfDmcjA zSxp!saJkGX0-I%P+6Wx%_QUB|7o1??=wU%R1U2KdQPX0C3ah9DDeq6LtVBQ}%$`wt`K>nT?jk)oMnc5&K`^y`%5JX)8nc3&CoW%8WhYOjI z5ESmTB60*tY$GBRf{n&$Kz!FTXEipHpz&86Ic4CRNyc&4jw|1AyrO|{k4hGC8%uJg z54Pz!qX81+!FFrB__*V|Uf^b;7=XMV$1kq5h#;fn_OhgkZ^>I~5*iPm4IOYNtRoZ) zfY38?rZK&FAejdpV^<7r!B_OQY25@Zjs8@GJP9riG>V`J$9*c-F(W@hB7-E^Mp6^? zV;V37b%{4g!lK?YC$GoxWFrU2*_=j%hMFd>X~i%8y+Bp2q?va}Y{J{K_TvMhF@>WG zER);`%%#3M&VCzSoO0d+EW!-i)T|{s6OJGjTIbA)0@^lrj)XW!tf1 z$=jLcPYA8BL)~aA-tYl_pc5rf9IwanQ6Rd7E+15gP_Uzd? z;fd29E)ok9sTvg(72wz3DRT`*$H%3>8{@R!uerLP@&1Dct??eowsLoVjs1>9jp6v=^-nz;p$?@3g((VuF?UFE^{ruDcJiPgK4laoJ9nwgvL z4?eap7--CVbo9ZR7dBHr_ww_XSj3>=x1x*%_Z4~Jzz2Dmazvb> zq2YXvl7_y0FGS4u*|Rp(z(?WX>@dylf}_8HGPCKN^b75hj z({j3qu&|4WQ?6RSzUp=i;C+5x-V)rObg>7!fKm0;{j1ij$p?}(e`b$BAdFTKWW}xz zwJ)bA4-$Jf++IY($T*$%tzd09&gLUe!vby^{bAm*a)I(B>mRT1udmNiL+te2FNoPk znD_AE!xTKAHLvWJ-p7HJNF#vM`844%A2Tu(x^a7 zD0q2!&*2v#ic}R1SU_tvF%URxt>!AV41yi3g0)+=Y_Xge*k;s}Aq}UF8chxm8tF4EEyI<9n`Ozb>98ffX8B?miJoEo9U%*)j;AaL?Jf zl;bAq??Z3zGljVXaSdZSU1k&mgnt9!(w)tQw#{-s=t?JSG?rNc&Pil5MMqghMPPm_ z4b5+u(pIF{mQv6&F@<6t?@r85j~TqTOO==W{q@jCae~vq;lv&R0fCRf5?b-eNZH+( z9@PgZWQ2eyg>>sMDs?E<&u*oV#BN?*o5E9%{&DgD`jYOFo_@P9n3y~+ z5)v5G_HrzjW5f#3E8B{nYk?h;X1^fb*51A(i~6HH9SzO!k*gu+U>wc14DQ>vFL=3z zm8GT4h8E!)Er)&m{FYGI*xKSp%>wpb%BO5s2oiNf4n!ON34ZdBu<%8!1_rv#^H5DL z8W>PfAnhuK*3aKxU0Aq_ZTTp;$zkjOiB;>yMn@%;l|4Q#Tsv=#)=X(1jI%tbE27h_ zxkX)2cgUff!e*ej`{?nG7iQY`ex8{a>=KT^`z*7KU%seR=1idQKq*F3UwJ}a-mCQl zBEjUPWKj`??A= zG4IZjWj6o`To_eUR05~T*~zE5Cdnx&g}{NW-QAOU{SVe`&PObYf`VPv>qD9?Na@hn z*l}!EiB-?#^^A<%&=cCwk?+Gyh8q3aWd@6SDGLh=GFFSQF`*e8`Vv$+hU{f;!NLRv zav=pFx;HGds-p;x9N4gN<2}HJEUPiupTk5&E7UA|8=N@17dqB^8Nc&1rr*>56=xA$yPBu7O-1-Dn zjb)a4>$GWG!D<{*KQ-7pdlpCV_d=IJ=`DZWOS?_XY7^=~S-cVE9!_p@W&a;bu!8P7 zpe)doYuN|{YX)o)6)Tbdm|ICnFXt?qDO$xK(W#wLSp7aj!^3&#jLR1U`S(g;IYB(j|L|dV={^`y?=o)x%Z@biw&&iCBiso~MxD|BUQpB!$#VPP@}KhaK@ozY@BkVBM#{14Mr=(=#;^GPw+`D%J^oAk??blO~g821X^7Vz3KgQ8ZWM7|$ z!)|wj%CKTet-W2}ak%0rhQrH9L=GU05AoF%u2O9Jvp6!a*Sz~wpw(ygA?0x89XPI_ z{5j$jK?%USk`Dr8b+27pBP}frPR{`19NkpSR`L;|&|WBsTYGWh=pMu}?nZ=%7GJil zI4wk~#*h7vWW)2(gI*}V^lDoF$G89WrGv)N(Xj?cm&$@A+-)#2K56}ZP!i+gX|`Hjy>`tBZJN8$GzHHNat33TW#rcT z+=xrYDKjA%osNu<^QtkL*-oHQKMd`>Wgoyp4FLd-0t-z!dy0+!{^!r9@Z1f)qC?i! z){~QyyZ;&n8hA=9me$rfs3RU-D2g1|?P31}KisWGI@e)=KSG<_-6$I~-ua0MUe)0n zlOZo(a*`{EWiL1-==Hkm&sps` z1|ZxzFyICsO&jbQ_4!fv!64DwA)|u7*P_4udp9-r>8Qt*Jl|K73XJvX1jp_YgcV_& zWW=zqiB_eeh(dxB4%>P)HMN?$x)2mv+0=l%x;jQ25;}bK>TbJ*xmyJV1(%m=J@fT_ z4pJYNlvIFTx(ej*Jo85Khy$n(H*nPTdUMV;5H-4x(9k6mT;6MdyI_=W0l0KQ-LU&P z^OlxfcDHTKA6NL{4!wnpq*syte#X3H{45449K!y`AThPd8U)NPVz(zue2v$Umbv89 z+S-b2jziyEnDy*r{F{-nfkq|-<&VQA8Lbj{!h3(-{Vb#FO3(>Oh+ketehv<6ceAsv z0Bzr`nUz)9%hK*@zbXd5jy@}>Z2IjTlMM;)xf|2gMB%75$w0c*IK#U=2IJJ zo@j=Lg*m)}G;}tjV-F5d?hqH(98JSxOmt8%!2mq6kvg81sZ4wPctgd4p!UvPyGr;K zv8oYouz%aw7$PLLa($r4EiWS<+}QR*hZq40B+(B};rJsY8m95_aiDtTj03DC z?}H@l-D+yiVbWS!-#xM%>2$5+LHv#GJGpsOqZ{+Hx6?8+sWSE|VJUhW`Tudj|N7GH z?0xngldFr%D6_erUzSzYAKyBW>*8&6$H5`&%eCZ?A9%!3gn6l7<<;LEtUxWn@DF5U6yFh@XS$12a7nkMWSK{wlQ<;oSSo+l?1wDWn}+uM~O zfrYE#;Y(sNa&qqg%3J&U=ZbCPGZ+cacb z(+v*~``JBIeR&!Wc%p%!LJ8M+8|F{Nib_&))KBH*X_!Ak;03M#1?_;lmZRM?nmIO( z#MJ^dp`ZYC$+pcs@k@*R>y68+)@JTxEqPb)Py>ldnVzQt$u?7_71qG1dFV(2cC*oZ z=t7>GET(X@A$yK~4j|t{H|V!zl-lT(7>mzx!IGw&r=vEU=JI}C?T7-$NorY-%(ZJ= zp#8rgDt1M!tEa~iDozNJv)F~vLdH#R%gd>u-xT7cgA$$|2C9kJ%sL#UyWGP3DS*vAl%g?=u#2lm}=+v{S*$?6uf`magIRakn-w}egbA|GvtLVhb|(R zKyxkmLQhKK_a8r;T3T8Zji}eJF9Zlj_JAL3?|6@(gdEhVcld}Qj4d<){3Ck%=W;12 zsokn?=#kZ5iOvClng8|cCN6K;9@s0vfPoDf{KUA^paifsRQ!}c2FKn`|WekgB2^&Ffl)(Baw*wAm}3% zdW7IJ+iCN)jQdw%B)+IMOJfT!rEJ z9$~9t2n1ACT2WNDZru_V7XD!IV#%Lu{jaanCm1Q%!u_}Z^#=Z6s}r&r4Qtn~Qxy#j z>OB?`CP-~P&DFgluT+g#y?x8brZ=cbdj#k@IHq)usQYlmoy(N=Wz0%^g&U!kn5 z>}Ay9d=s-&p_6jGHpIN%4}N?;ApVMPWqtzExSF9K-?)KOm3su211N5V6Th5^iAmmf z?~>ns`z_=i?~?7};>;M|2ar~PULOLjG2`?^5du73O>KI7`}WabUham5vXMpvF54^C z&EJUj>D82@N$Lq&_z49C)r`#{>Kw6wUERlkcS(WJ*3lWp!+To6Q!S@QIl`UWCg-4x(fY3b>Cc!WW$_45Wc9-eg&AP!4PF2~9nLQgpS z`rDQBv8t~x0bXHswxWF;h1q-Ya#-_1-HCAJ)vt7F*lBuLd3o1ERdaB4eGja=frZ5# z_=#F`*nGpDuL22^VKNKX?Amm-~+pX1K82-y$Y5J$u9)Z5>F-I2b^$26=NJdcZFm7 zb_&c!b!h_>oh3__tXj3|PIB^r2!%j$>73ae$dxJFc^75x3c5r zisNqfsw(NgmUe-n%a~OSm(Yn>1xI1C$Gvcf5{$kL3gjv(D$kn(m|Dn<&p|io4_w=H z&stmuB|4ul2oUnT7iw>3hIW7$;za57hBO)oPRRf4!lR2u;EY8F|>iSNZ5L3k8;#svsp!ejY19O#5A1gQ43$r^eH z`_g60;xD~hSx{8eb}G>2OIq@D9Nd? z6Exwee|73#|1et1|0g?V^|*qw05xeQq0j4vhHiiq?QS$(X>V_jBW+4IvM)qSojd2N zeQ7BU3m;Zj4`{K12vJjC-G3hg1X}P6&>W&{=b^Jh zL=@S{!O@O2N}gJU2jiKcQPCrXI=I3X7Pwx;GrbvbIw?)5Cu)%Yz62?Q=+W_?E`Wktg>O(ml`Fzud>jzqf@0N>YtMoc*~ekp zf=q9?cRv|%|NqNNXsKvQl_!&b# zf6}TCc6B{MJzxw=1?0K(yQcK5_fThNCq14j2ou#EQa{6GL@^gyRa%N{&>^sc^Mt?JbF7TXUhpbYSwsAiFuI`#USRpTSyiR^`B+ZhY;j#dB3 z-9U!e=+1{IU!UfJG<=?KhZ(SW9{ia&y>jO40c&XD-L;94n$;&#@o9V1(>`xE(2WGX z@I7>s3%Ip1`nxRB{cq7dZ(tJwV{$htJRKis^zXgvEMPX;t%T=atw8fLHaMrIMgltN zKbA5|9-P`*Zf1IWe)b@|uI=6VoTz)}a_zF4ntOG-pz=V5SqUE}HuV4ykt=lcj#q;U zW}yJN7jcB`?lmz{QD}})=B2;x?LXfM+xt+1mZswp9-Xs_nz}r`tf4{l>;ngU+hICB z5i}o<`=ZXiKf4Og)_Z_4U5$424f@}4M_Tr^Rn3rj5c9o0IW^T|%L9c9g1`%43bGm3 zrASwP0=V7_*}AVTS#HqaupZTt4aU(@iwsJB&B zsy*YmSBatpit(ts`|74Vi57692^~$GQ6*MJS=~H5V7plc5`EAWsba6hN<)BLh>47@fy!1 zZh;PqX#+wDLF*-k+7RkT{>P7NfuB!jWQ(?&iIx|4z-qwLZgQh0-hn69WZvR~#bZ2V zrwH8+QdjSR&q9Is?k&R^gpcL9=ZS&^yufwsBc6xR^5e%V^WHya{nxKy+kamNf`WqR zD<9&aCs_=QkE>=hu4R*UD*zr{Q5}0O{^*U*VL4$D5p4uJ0hqZZ=j7x-(Me~uW#mVa zWb3wVxDaY6t%^_PJob_)n4TEq>mEC*K)fTwj4%5cV7EY7jBc6Z4`^E;y_9nOR-%}o z8?vv+!N9<^BYH~Ixp-C>5qOBtBlT?*D=6@2$9E{fH%2;7W?4_H$Afky34GJhxd$pE z`%D1qB_<|j)#}wx%(qf+J9Hn#mfQAfjxY|}kWPqUdGlc!QvL&PZ|@^jRzodI5NR-u z8%lKl1|B|uTFQxr zDAn)wa6aeyUf1=xK0p6_&ha_&e!pJN=i~W!+}Crt7nS=ro4s$no&=5T03tKBN_m~= znp60Ft&-^%UXXFo7o>h8{bt`cD=wGE1m;5wY^6xQXRwK0TR9L4a|5URNj4NFfQ=t> zp3&$BQ+2la`gSl7DwhV{1Vp=Hz1GIcw|8;lC{yc(CVCWh9=1q$64TJho!n-{yuRhx zrM9;AJA=?yrUp3KrlgXL;^9srG$!dpInB<7GiT163Wt~(!;d&%IPBk-&_BO(O8M7P zvOah29DLA@;8(me2O=XaqnaxKs=aA!glE$Q9 zm{+f3%t4JgJjux9;GNg6RpsqrgbyL;{;rDKZY7{0<1+&ve>mCLi%dfhbEt9O8oh1%VwmJi|6s#UAdR*mB_Zh_c`6IfdNA8tgS39n~Eb~ri$BQ@Mw0(iAAX) zeI}o3LVespalDh1qy(%y^?Ixo9_P6z7BkoP(oxjZ{_-WJ3fU2I-9L=4(W7{gCElUt zh~HsGfXZ*5?aGTY<$yn&9X9#g6(S5%jXx25If~D;O3#p^p^=iW7fdkv*ZTB)v0(=~ zfw{^lYZLCU}W7@cX*2Ta5Z~0~i<}GWr z3M5*{|7TCSv~_D^D#(EqofMqW=@E6O?Og;9A?XEu-+XIKhvU6%Ta%fw9xD7Lqt~s| zhiaxgG^V*8UHEMX6WB$&s0q%af@cb71r{32!mw2L*0x87#oQFS=KC{dnv}@pc0qZ> z@^uQ(o;~pFH`YxbyU2_7mHtPL?0_CFT-D;7bW;EVKQT@>vJGQX#q^q~{VxPQO0|f! zq&R*0^!PG@@|w|=zl<@0H%7iSE6LP96jf|b=4aTPJ{U|cdj>XBp}<3E_wDTA&k?K2 zLh-Q-_4E7-n4V0l2COnmr?R%ddvr&Bxt_)d45VGNp2zqQ?p3!wv{eIQ2R6dhSJh(2%!ei>4>{=NbSU@aZ^n#tgP-KSZ@ug?$%#n!++JOueMY}B>(`ePp(=2YU13A zpgihP@3!*2dmx@*?la)EwNNXr)eXu=C^?|dBo~S`sN%k0T{c$E$B&y}Y71`~{XG^u z`XkOzPHm2}d9=zNcBTS~3k;6%aO%}oC)%`aE9yJd*<*NHjb_iD9pB;Nh;`owZE4x{ zysKA0KtPk`%^$0D7+Ph+mf%0$=jT`d+Kiz7Yc>80Q~2Ni@bJ+u)O-xdSSOeuyq7en zUmtT=gHVibTDB327p7fmGgG~D=N-@%bQQgs;0I_W<|yl0E?Pa55T%uavLdkOrPF-3H#Jz_eXfu>eIoVS(8)Z zmDIK6+Ujr1p$ode99lXJ7{lY({O6xT>B6+#_fX+avU@`k-rVxEa}M@36gV8keS4h; z94M^Y{h?00kjH_#zlYZ>eDLT|vz9Ge>Lvf_Uba9E!@|$ohVYya6zL~#Roc0ad4liS zqkDJbup&gw>LfAtDQ;~=D{q6xKyA5tK=7!gNF_^b#}fof&Va@H9Z&<&3c}T z>6+^xgm7ktN?E_@=-lUP@LlKbH8hMW3-s~v37+(jRHZr0)ZTdr9O|>-H6LX^U!r4T zV)6xtY|`qRX_~|_i>{|>Q$NoYeUTRXawPS8v!&aK6DQE=9M752{p~eEyc=Ly-@nXY z!r>w20XbH)ZmzFEOxIc-9tSg2d0A4vdE2&a#U(2oEVXzckZ@qVL;qIV{_8jNzPqfq zod}p7<5?YR|rA%9;We8fiw z44cpPhZdjN7JDmL5U206#d~Odn_TvUp;#8%xM9N+0-G4E*OxsR9Ua}uTNx-Z9P~vh zC^BVbDxpSQy>{Jz#L#Yc)L(O@NBnxr&m;Q-kSR|&le)+mpjfqtKjqlsy5a^>Ox~fx z*`azIAbTE1H?-ownX_iSD=IqbxMmu4S41S7G<0Syot>TIIID@OiR80dV8DoCJhXuW=Mj;`kywbvDH;oJJg0U zQUv)_;abcoEIeFPKTcr|z!lu-i^Y3rtLLyeES=1UakL`xu8+(Y5Yk7fY11^jlu%kR z!k2f_^veoIKw`qAXmla?5hYXlub6%XZ4?9+`uTljXx4YFvATO;3#MGnXNKgbz26@^ zE~lnC0f3bFeu1% zJS=r>;HA_>{wqxP(uXyRQbi3K)$vw5L5{8Wx=yEt;o*W zo|u?uWu)CpLj!!dK471`G67v-Gjb0n)c+5Iw@7C03NN7liQ(RRDH8WHZZ{M*AS^N) zGp5;PCsI+;26#;qoS)tUh);+Oj+jQa!~67Uf?i1OJu%yS)o&Ogodo14{wqGTWivkD zt!w%YcgqMxh3UYA=?u#G>~FS0g) zSEiW$;QoEbwO*E%JBraxDR=3zlTNcsd9+Xu%o1k6TFT1G-4^fRNSMoHe3xeQ9WkB*c5;MysGJ7oCj?UdY#FSF{Da$ z8M(SxS?4rAjh~dZ_zmPD)u}B#>!f8RncH{oR#EXjei**BNOjTD<;!;?nsGo*^`){h zL8b{B4YSdsp@3AG*6w4}7pQm+syarz=0}D*T`sHj@%B!^o~Z9tJM+QGBWt(+DDLNf z>CymXRVe;;;1ZIl)Y9p9Z3kp1^cJz3H-CCa)b8`@fmKsnT!#EYj{+}mjKBVhnT=W! zaS5MNyl_7ewFjM#JrdS}?%=@kC+wI(2$(a|xu>H4FX%^rc(fYvjJS>^Q>VkcT1Iu- zb|~GU``nmg$Bu>Ex~uXDxLxPtTw@MHqFYi(2PY9@a@yL2O`8;v!l_Hv6ElNr&1Uug zj>7-Xe|G6IFSH8_aPg)_TM!I(n|})Y}}Eg$?Qd z{Nt(0RSg<6pnGq^A|qGPe#)BeN`F#(fEvda{ua@$Q>VVV-7JCEFMC31Oz`B3s&Kc% z-gbN1gRS%Q-t)>Ex$iP!eL(dKQdm7BGj-80{ICNM6K{ijhdeS#T@Q$>m@d&Nnc25t z5o5c)#j-z#bkD@&Mz`&K$G`}-RMVGeTLHN+lgTsNdU`8oGM{O`PYp~BSNPUkNe4wTIUIZ{HSJ}hiP^<};!^Rhm3 zS$B_hhle;yPhef|HYqjUyLa#IQ9bL{x|o8&%#F|8XMrIAC7TBrpus!AD?rewH>ID7 z93{%i!DU5Bl)kwO7xrFUOo61d_IF>7Hx5^x7|2ytsixiiuG6{2&zhQW08Cn23j^80 z(3+f?>F@gFwa^V(dcXAR*NuRJX<2DE05pX^j0n0x>D^8CNNdp;Fiu{ff8;1e)vfs-h5e)&wmj}LAM{6+yNFO+QAR#l~t{h+B`8}hLV_GPviFO36 zYD5@7Yn=u35_*mEi}{L_>lZOzPZ|x#0Yg8i*pp` zy}55iW#z$}5=W26V8WSsC#PNDTx!bD^}V`U^2hy-sRQ)rRqUHCYR1SI_I$Ou;w(6F zkpbqXdjcTR*qi_WI=-prx58(Q1#YBcxq@OVsl%udu6euO&ppW88^=Ji8zIO~0`u;| znsJ2n&-zItPB>}{K&7pW4lZzQ3qw;wTJ&fZ8rww)$Om(xN^DbsXeMl!x( zR=e))+_lSpKtUQ3Dat%pTb;)I?sTNIHzI4DG-+=^OdW26Bu|xrGi{tdGhLb`A7>)R zm!v}ZNNbhtCE6oMB4s2BYj54HwR$j0FY$|Ft(S4x+kVx z80`44oj3-w5|$&F5+W5ZeRKN4yTC-|Tldiqjd#w-<i@lt^5_QAby zS_cd)8pSQaE6N=^_8Ipj5C7yO6szT*v!*|frlJZkM!;oca22wB`tv6_8!rNyx1Z_+ z=kGdaDX27azT1(z3|}&*?cO~;KnGl5QO1?-c%tPF!T7gt)sb+wjeW^CpX2DL=@Iz+ zOAi1VJerngK9tRdksd{V&%I2I*SNcLnzjge(4|!W@+l6s?juIn1jGQzT`pf;K9G7% zH6HEec6CCTQ0e#YYi4!3EUpQ9Bk9=(&Wzpwi@Bp^!n5hYt0y1|b^iWkZgF<3UH-fS zZFi5}|G_t63Tm4I9><{lNV)3&=a!)z&j0^R|}} z{qMPFE8DzB7~$;Z=0EHkFO&<$u4?_Nsv=e6qko0ZTIW)SCQ`&2cM}rEOtz`vsGf9t zy=PFhDx(kF>(E&IqRrueMDTVhhER5m=x>}QhqI5VUAud43k$dbMPebxWZe>PnB!(S9!_o{KxHB8^pVn)iDi|eg|4)bvOzWJ*| z(w#f?IXx4Y^f9t4$2Q(`{l<-xJT>^`TWNO5f653pE^a3J8fSv>d174{+g(o^JE6vr zm&u{lx?MYss;^(6@sny)2F#e^{F!0g{7Dc<31#Y`+GIfd9gH!L^m=#mN(qH@<+Tg9 zOxGnGqnuK!|9ZWhBUc6qnshT~=RsU-rm-fQ*ZRp|#PI$OPd#~_)gL1nQQ{D6bn8B! zfg>X9PwZg`(-XTHA1p)_<#gqmVSvBCA}yUoyiOtFkxg`~cT!WWokv{=4iZd{W1-fiB(>xxk(cK4jFpt0R*Jraf<&y8=QrgZudgMq| z(sE!IEL*20Bv_y!U2UOugNT42qcBKyyeJI?Za^~;xR{w!ka%h(M^p2_z(BLGGBgOb zd!|q0B~80%)BVSf$ro(~BW*LjcG$DgggPt7ZRyYH`7@1-Mw^?XwtOnDlg6b*Bms5* z;g_}jgI`1BSgS+7uV{M1%EF@c%>KRneLgM(r{eg3k0ydaPr2)R&C!1kU?L6+jS9*~ z1T+Wc7Dd0_lMlJMn`Nq{De_JbVWm{0YkLNsIPGxf_PtHBdiCyo7lN3z=C}6IQ%Dt% zA+4-++%b@CwIJ9>2|o`=Z-sW z+!)DlrQusyUVaa>PIGJX0JQAXmkv6ktp;V#kmu@7I!q?9lE_!c$lB5QAd(d>h*TB| zglz@jx$2Wd7~si^Iy=~WN4wT4-U?)l9@@$e77 zr>-Uqnr)o;)@SRMEfRC%_pLYK;fhk(JZ3Vf>l+xXnsI}7DNrprbA3l_dY+Nd5r9-h zrB72a$vyjFEf8=$ZR*6P(9mBV#x*QwU=Z8n_+- z*MIs|^*8)6_HnBu*WWWj55*1LGApkouVN#rHGaaCKT7OT{EXKw!rmkDSx)ZXJs!C~ z1d38-EOoz04J9H1&x*$GlGiaq(J(YjFKUx=*7wRIOc-Ek-TL$y>oQw+`<^{p$llX( zWx(qMA>!vd4|S?c0(=k2V*z?-(=|ecPVu}UV5i%LAXSk;E`Cj}t!Varw$4v%oq@Nh zzjP47!YwK(L~wjL@j7-^zvJ_0FL`ZZ24D4;T-+D(>Hem=&WL1Q&t4!yJ=9H5{@~%m zx5AN#z>&#&>@i8T3Td#4d2c}UH+GF)$<(vNY;DWldE>^733V^2bZu%n2R)_bqR8Io z=n5;#ra<>OUkfrFl8zE5x?nKhXVJ6MyPsCKeVCoCMs*Tq88<(Ix*Jw^A2rY1pC)2| z;-nMx&R{hXXo}+BKi0q&NJN#g=ck_yv_vg@+NAG6Mtv&d=;wz-<(Yd(s)UdMf1hy` z5nBvJOX{q=l##(a`7M3Z{cqnm^N^eF=uFk<5|m!UQlp3U^B76t6WqE??Y^E@?ahLk znCsU+UCl$hbOpTGx`Xc6>0_xSp{gZm~FG~96NX0kkZR3a*E-v-oXZP zV7v)2X}Q^D3)ei()6-Q zo7F#91~TrsP1-4Uj=t1YctwZUubPM}3h*Rf3{x~@7CB7mW3o}N<`I)yf_9j!p zXH4;ACZ$@LRqB9j*?#N2W#7Km@cJZwG+FlQsuF0!J$8}vk5Wgp4-M&%{66dS2@}~B zESpAAApQh!X6XZ0GAD;doox#qXkqNAaAj@fiV=Ex57Wd@Y`%F&gE+E7HLJglz&0u1 zy!dbef^|*PmR!sDbD?kWjBsB&I`q`;SuU0nCbTygU2wliM5$&_{)Wnu&zmjlv|PFW zjy*cA^cJ;D2ndUL_Zw9Lq?(_}FK2&|b!`>o?fLohXEdb!qU|47wzh3u6?FDcGgVLh z+bPKrAV!V9)W+!ITS|I+H!~~iZhHFkv`Qb~z7Vw{b%>V%Vw?tE8hgyNrHQH4zl?DI zv)TOhZ@1?EJ#}r`vgXAlyXl3~jJoy8CITJ#Q8j5i-0&^Y(jS^wUd@32G9)axiBM_;Y!R^sV7t?<0C-I!bAiqFi{^l;ysKYn)NbZ8&tOBkpSp zdsk>?R5Gm%p3+0pqlaW4oSzRr&le0nJD5Sa-OLN)_9iEYNExi-BpRcLCqKfOBIZ&@ z>F}->gyT@W$6TVA4p5?w2vK>;S_GIHnwo{n-xd;S)APKDz*$8v7C&K}oSHTQ-(ZDX080R@%LoU+s1W=}RL7Hi0}BeT=oTPj!Do*uQaK57d-F>$V@Yc?>vuv#v^PkF$%5pT6NNppd~XAIHX5xNMr$)Kk^61>$q7cGr$` zo=M?#NpS>YyF$#MbPn{-eosyJ?c3*92{i2_I<2%dx}lA?F3^P7cHRRaLqNKgFx zzGmjX+(7?lBiXm`(N9MM*!G4p zh?{crC4Xr6zjX2Qz)u)>)qc@|czapg$|_Im-}FLSooAI={pZ!In{MB}y)0}aN|!Cb zdO#y)yW5uCylDI9X|32jopqW5hM7~~9S>{wrri(mQLi_LOt%nw?d8jtH%w>Kz4drg z22w8KLF;+1-@IAX)TgDAlH8wT?puUMM}|DRL!Vd-sTZber+(PfXu+8qvbJ;@wNlf1 zq-J4An;8%IP(CCoDrz|4B);^XA1GIrPE+^NU|Td*n0juCd@CSv$HEV+NMMQI7^}^9 z4;?&sU+%S!K*eGQLKmx{yKcbn)@}Cu!5&L3dsuPUu3z}^lhSm1lza^h%2!x9_aN$3 zaX7PZF~*CNCnDlu^b^hp;VkmD`}!t;nkc?lkrg@KrQ1r8{m{%>%p3X)lp_9krki_M zz=VQ%))!t_HZ@&WRemn#6>nJ%&C<#qY_J3l%%R_=gqSJG5rQ5oBu|&?3JWAkg6yd| zncDUaj*go-gyRv#uv5|NKU!%;uM**T$#>hfQ83Db*e0s_Ym}ixCCmY3;}+bZA_|ZA ziwjTx!>>M3f2&qB%Ex@<8BV|cqSZE~)f_Qvz7-!%8?b*Ab!argt1FI=+W#uQx=@6h z5pIM=1@!+>T|KVlHdIObgVjIpOpS3}XuJyHnP0kcoA&z$UYK-WR(RKu)7+E~i*{WZ zs@UT->jd(-r^jpPx+{3q+x43=Hqu~hVb7q^CpXH|g5?JL!-so^AjCjr$yjluj#k{>$^aWE=uF-847@VKdPvAMuHvim1o40Hc zGnSwCw0nwEAp6E;-pGuyFsY+6V*`8#P)`)j17Ax&W}x*YQx3!}I>8!8e~;&m+}n_-5f5%t792$4{R= z9dT2=gXzP{6An*T$uMxY2S0W<`re+iZ?&pBb6>Cy?z((F(?R>R^s!7>N$n;2Zz#Ik zcf5jMi2EXhye4ri2Zx4{+>&M=>0dt;UQ=&`K`hBb)Ij z!a@CQzx{g{#Xp0W|IuG;Nh?e2R8!9Q<|xnF#*GIXIF0+i8Db%BW`H_RZmSRHYR#Z; zOX#fJi#hDgUL4n3WjT4&DTX-?l+b5}L(3pK3x}_bh zBemZrPZqAl+EScC!PIALKBqO3r3Pv*k^xYLiSSmw5w*hd&95@igsDH|L6a6W6Vt^49J_BWVc2c|b(AB7gyw_4zy-INLiL^##M0?UTM+V)yA7Vf zekl=j)pf?zMH?CG_l_6c8-m@Fb9EA%Q){Uxf|pF_KKD7M=Yywq zt_EyyK=SV4UM#s`S=D&HKtsi~Y5Nins`sZ;fnm+Rc3Sf~dL=A|RY2uI787dhw))5S z?tNZ2gOmc!sa4rysP4e*+W<|1!S)eGjy=7KGBbwt0@1n^hkiFi3_R?|-?e5^HLMao zXZ|_nOQjLWy2?*?ywCsPSD&!Sf2D_;nQ0u}tG=(nC^6cssAvG|himeSZ`R-09%tRM zG~%&@Z9wDH04OPesYo;VWa=ViU;=QlzOkV*d!kXM+J+=tb9&1IE>E$W&4)H&*jowB z0P!{o^A(A-BF#&oA5}HgaB+YGi2g?PQ2%K_QzqkfJJrLN5zUA&wcnJp8)WSgYcEZ- z&KNK{n$sO5uH@RlBie~7%k4O%39gTgXFq685slRc7)04R;JcY?s}J4sF=V2Zh=_%D z^XAXr1h~+oWy9<4Wv6p0YHDgwIh=o{#>fWyxEascw6rzkRUA5cbfKNAbR@|mv1VZl zlH0Gd^cc>9zFmH?q%{;LEE={h`EVc%Xo?q!W7xlsb$v?J7&;zn++Yj=@A!5jxNA^M zG`cxMW+BF##L*sZ_J00 zvqiM+Y(4bfjgfb(GF*WlskK2Ka-h~zj|T*tc0GP-HkSe4gE;(3V&Gf^2I%fyJklfU zMUwaj0mqLRIa1O~tUZbpvbQcQa z@n2$Iae`9+Aa{lIN5)h1{5B|r>gkf)TunWF>lK_9h^m`jopGsn?mUxh-<>UPJT8nq zXgDjz#e7Of9R=Y!OFd?Pa&MVZval0%gxZw<-98byHPU|X+iRoowd~R11UUG-4A8OR zR$P^f;>F80atQ0X^C+3grjO=q-wnoItI*C@>G z7}|vrNG)=?h_*i{C*OD41(ly&lY~8`w0yeS7-E!qC;D{1{KSD8?boPJ8z}|O6%CX+ zUKYL2|H~Kp-?m?$;bO+LRue?Xci|9ToZCqe;8NmtE2IuMG9=;=q{G_K2escm(lVq$dR5F{jH?5m}# z#g$56$0vPXcvd94C!((Sxn1AVdci^lGusX@NM>=GyyuJG0JiKfY@BO$GBxh)+u3KF z7o7l?7Jk8(>wh+$^$0h1ITa`dSvWaVQd{ZnihWDm02A+^fLST*M> z-D=k+8{~hawQE4%-yZ}gXwBmC5}DbTFoKDsUNh`R=&^i!#1&Px3xE=7<=all!bZo} zHu0_wy*cm+mgkbNXY_~?ZB}u8^mRmUO`G2M`L?dMx1QG2%zT-DTUZ8Y zopUSFYuh>JXkcLT9M5idaO!9@o;3lJHqMc1vd0Q}HTwGcX32du2Fa&oCAZTFV>HDG zN8RYYI7~N_n_{%eK)8RRIrb?0db?ajc_>Jm09nql8Nsc(=dbDOd!r7)q7tKHg__&B z)I}PbC+D?$FZWHwe!JvEE7Yjjv@QtVcEQnSyKDjLy8HC$1ZPQ@Q%ol}$HsX%D)MC; zhxn=)c$2Jub}*+OoTJ{Q>i74XVTGI-V9>`AGMiI(Tfs^PpFlBnl~1!!wuRd5jakOLJQ|j-vqnK0?kt>HcK|yD_(T6Q z2gUZH*U^LfO~3rpVifqS3A<3HDBG7#C<*uI6dt$RH2ITvuV2<#-REFFv@Hj&W*=O4 zZ0*&9*PI*vD~`HNL0_PVnin*{@4?O%=HCN1Z{51{JU98N zaQ6DIuxsyd=LgpRK>(AAii%@rJc9X0Nw*<9d}V+RgwN(nmoA-kM-=Va{7Z{gtvYml z84bxf<)V#sn!RgC>Y^*SlQZ+mzI+iC)`f%i&onhO5)CQl2JiCndM&Zq*Ta0y;>CTV z4bM0qrD5!eB(7AAV^Dk}nZDx};Q!NHJahHjc_2XE_2;?v=X?QH=`#NkC&|v1b~;mL zoBWcEFGt_4nB0{agf2R$vXnLi+QX{6sDhq`G`&?b*0N9C^|~cv)-PRZjn?68Rq@jg z{%Z;DX+zq*s2#b?bzEt_I7|en$cxUBy>s36tc(Yge%J{}m zlHgfhZCVEIC#|RHWlvg^$jURPPk$xE@Bi!G;_krh z+e#17ST{M}p9GAwOAF3p@e#Sp*})H1|%%tT~n^pN!$H*TCdugoR-@N2vjW-pKz zO)U&?>^Uq@GC2`C+yyYwxYSlzS%JaV_?kn5L&G|^z}FYB{0X1M{0Zs{FXOp-lP_WG z`=$#MdoF7Iv$VANt*S(kTb%i)=I-SL*yX%JE#bJfjuTO%Bt9#v3sv@DMfCjMJ$jg= zMblKP~IYb4a3!$;}jKK|0JV%FjPjW{)VVzuvGv znbJfAFrlcIre1YW8)D8(>hA9_`z<1Yu<`(l#=wRTAWT3U@c%-x?n_$`tJR31(BO}tftUO}jI5t9*_zJss(0f}IHgNby9NYv&R zE=DjH;$5|Qa*wz__z(TE?V7cysLnXJ{@7ZIbg@B9bo5vcCmYNd@g1Hf7P{Bag`^fi z-#=Wh<=?CP#tgjb{l0hIYg9}!nJ8)|otqMpup_3=*a^NtczOtJv3O-l;1^eytg-bS zw|b!c%|43*{aye6c=$WNibF5x!%29>r(J)c)dKjSydz&;KaotV2~ z#lR)6X|E2fFi4LE>O1R!#30lzV0X%#K*_Ob*wA7IJ6M^nTX`l=S6lDrJ6>vLwinb; zysNE^%^bvQe&71tW4Va@wE89@E58Q)T}r9ww)wx~6UU|bF)oDW*VcLYgt?KE)zby` z=sN7u1=LDoJF{kDtep6Htwm#Sy|FHbyf|7L*@cPynz@qCOqy!@nOwMRZC#d?mDTL> z8>~xcMYPFJT~rSp%qOtfR3W2))4c-e%Fafe%X_8%W3NyCzF zCzuu0VI$;V_DjHsIG`dSsiY)pYMN^oQ5Fl9x2jsObZP&-`XHGtC}Rghr4gkGea9J0 zj>ahJ-Ky(3q87$_(f@mOKrxL$BS5M)Q~H0K+55L9Gp4h@!~%LMJhS1Uh56$Qlk3IN z)Xo2#faHUJ2++5;8oOY1*g>SO9j;x5MHckdpnc57L+O3PPkiE#^t+bMbNxW@&4FPr zp+mn^YUp}4GFx@; z!bN03bGNLV-(d_tl191mnJJM;|Vc^AsA0%CYrg*$IvRxX2iXma6 zPwrcFi?3DG&X8NT#>ZdFZ0LzHIliP`?FFy8T5)7vOwi~}Kj`P7qoZTafHpL?gd!M{ zKa%l|ZMq;OcHsid=^aO|zG$5O_|u4fg%aU=Hh&?qMzbeQtm=L_wo6iGQoNy{yLklq z`ub|s4Rr8%kA&43eh%7VX8yHRxgS3GP|PjWv^5_yOA$`Ae;57Kc4*5U^ys4Xa&vp0 zHG`lG5%S|A*v`!Zwnjb=(0Y?e@Vp~)s`M>R)hg#f)Ch6hSN?hjv)m6)5QWc#4Q<7h zBVcS~vfeLDKQWLR)YiH~rSv#wfAC0qJX*hbtfi%&+pTfED|E{=kHaJ$Z=4%~Hudx8 zOV47y5S_0)^XQv}ho#e)5yik4(RJ%B<= z=(9?7(lyT%R0xO*g7)#z!sxmmzn@GMl{(UpdP)C&c>MD_?kayDB7%gS@6Y(lA+i+c z#P7lK%!u5b&2Q?vd~6~lB+bm-)YR6f#bjn8l;*PAQa=(Kc2?~(QXn0rAKvhAUWc-FUp{)8rSq=>?B@lS&65D`G{J}kW@ZkJ;V0T6_+w%?|N=+|)3`Pm9D!7>c`_G#bi#PW-OfQQ%7hRJ z&a2yzIa|jMc(-oN&9u+|kbZsNmsuF~=uu_D>85o4Sw-W|RnveDxbb{cnvIdsZS7uP zWJF;>Np5^jNr^8gm?XbaghjR@dyxL4>H3);djXtHsbTa!utIm@!-4v~*=41rcL2}2 zxvvA+Gk=kQhp5vx2?>ucLv=ODDd8hc25yYY*?AMW$j=Sn7&XewdV@J{@KO0{xlRqU zpEXIKTAs|Ol@ABHjaFynX#NO%&O^6yqK=H=s*N0XtMTLCy?^ibwUUko1yoyZQQIEB z$7jYR6h7)WJLX=|sgt0s9T$%tV0nt#^!Xvkj-B>ntP=ep2Pk8T>9--Xp@~C$Hc7ZD z(yJZW;}*cjo4;U&H|Tj~IQL>nlo2zzBAAzLRqH!Qc-aPKtj|@)##v^Bp77a|_=Q1r z3lBxCrqSzu40+(2p6sO{aS;5-5TsM!HIX0P8Gc*inOgFg6MXLc`HDH7Ycklsl1d$0 zJhVSbj5t1(Swr3R7a>3W9-utH>(*7WiHfV6Hfa)vn|A);M`>x3rxXlPSjWu=A?ca7 zJZ}^yyqm}Ny~k=;b;NX8boaP2RfWv@&4jCg=qu1q7DdJP8%Gf*YL)b#6ujZ=Dqy&N zk5(owzqfAsh15lg$DJ7Hdf9o=w?c|3=c#VWVwF%UmOj_ zYe{MPU%&3!b;;d&&3e5;t@d}ne~n57FiDw;Q?$PIi4%u5V;y z#FA5>uv;DP}nX_O)uk*F=T$WKfqjoAercBRq0;l7It{76808!8U-qJSTzvr(;kF6ZB zX`R9yA4AIb!CX&nMfJP-4MeopBehN72M<8jHFsb8^Up^^?tNm~4atMTdewWQ9`N>E z4(WIK`7$rWs%-^f6)YOIj?|mX)bypN<=W;pD%q!|UHHnK$=mHADD^^2Hqq*>WJjnL zu>?es0w%cgQv!o6yE@?WwfWE+A)k2F)k(|Q2}1q+vf<|s3>Tj(f($MQv<>)iO7ToC zS)5^OPrVLB7mNN>gO8wyog(&PYoc8!Ujp~|J(zvn&EomO4Dt(tsgTEuU2!Lf2mvIm zA|lz1tjZCbAIKT^X&+xPyXQFfClcidcUskJgb8dx`mp}q9#Z+t}z(y zDfCKG@w@JlYq_rC#dM7BUt{|BM^|ENA8c%u@yYAQ_wOCGev&)i96N=-C9oYZPKYq%6@qd$At=l|kaI-FanSGPheQhH&UWU!qb{FFzcars{pZs`z zQg2<9By;(Eq!IY4w?cann)Ngl3C)!`@%2+bNVnux6yu$-!G@lK^QhmO$fqJUt_}oN zifH&+!VRW)P7BMIc2PfpdC1YISKZIDR+ywouKY~mIe zEra?cMq9bJv@;Y>2X3G2yL42Le@X~YmURaLdlum701&o+BHA+3_11)AdxuYa)`?6e-V#TM|PhwaPs(K!ZK}d zw{6$?VP`Vqay=dH4%}Ed_w<&ZGnX_0XbyR5kvXgRyW1bX>%-da;xj*roIbe2QcN9_ z11gnT*wuuI$5P`B=O9I?{mr9r500unU9 zMz=h67r+i~>{0nGZuVSuccS4-N`b0r8;*unIK6N~HbkV@n3JkkVFvN2A@rzHHG$c# zi0&B#<8;ppNBKkj-qhbv8UHb};o$|N8L;Us{nCqHTsU}E`2=PtG=J|IN|VZKsc#m( zC9(&a<|jRbBrto?OzD;c*OJmLp(}qY^)0GYnMvwl7858RJY7Zt%A00Z_A?8m8_&ns z{Py>vlpHM%-MIjn5nJt75H+}G&z?24KVxzRJ3L(shTN_^@FLmF$O(3lMabGUom>z? z>j0M|8gnu6kVEw{W$36^_nVz6*6dB)d{(Hx|1jskShfC|>qb(g*m-+bwLh zIg$>i*zz&|%nbkbzy4W?`~NaEGi%g+Shp|KD^}+YXn3*z@DRox>&YDo4EuK&l2hqo z^;NzfRyk)hP2}|G5%wTTmH0V76Z7)!P}C?~NWoAsSj`OvmQlx-QV-SfF^41|=e+}> z1ffFYuwNoqDzC5cAS~KG@`q5+ObN{{8xhb)@JwmaXLz@E`db0MWdSX*nkHmgadC4B zsK-TS6M7g)4P+kW`U9JCRlEjl!ai{M=Uf#EDH$5RNSs73IH72v+(_ZQ6ffSdOPW1q z15byu1T73iCeCLged(OmT8^Bi@Ju;HMHkA1FlN}H>hCymfq^096M>+EfGp&u0Ey2- zJLHU35)$`Ud^D&C5V)^614-lMI%0K+8^Kwf*gl}Y8Fv;cckMbLI{VF=u23997x}7} zVR@jEnERG}`_^7%PuXD5RUYKFrcuBh#`K5y9d|%4NjRs-FX71Fm6T{---?^>J=a?9 z%O6#HX3PmFP4k9fD96Q~B5__sL$fiy;6@c`lVrC*zqNv~MOQNF(logbMKTXX6ULUv z?Kr6aLE^+y=bZ{nFDUz+J9xw%O8inB4NN#^aHd)pc98SRl^estoc0`-%cUb)lvmjx z@-nX|C4ls9W@aZ^W44WM?a#7_0@{eH5;SmSU9G1?cX6)kKx_zWBH2gWh1GxvqkeT8 z>$dquPpvCm{yp5BCs zOKJVhrNcVEMgW(v^VocqR|EZ-Sh!B3N0FhNj4ZBZRn1-D@U%*nW<_55m>dY>YYbu@ zQFF8J13FOdQB%8UA3GyI{ShZ)Fo^&W<=7+s!!Q4=zagEm78dcGx{5C>mAsRdJmJB& z)3TXVGS`AC1Y@o3{3XXYDDNxr;LwS1mNFI)3uhJPS+pNwSpPRdd?ls~E1@_TNjYf% z2|8uII=;`43C9L?Nsvy0ZXl9aDGi}6KFb%!>z`T%f&9yLSz4Ob zH}N9}rC$QkNaM7_4iv`gnwZ!z2M*$HKC6F?ErkSU^aQX59?p-Og zqLV?w@9){_x?gFh-hKO86h`*#&=(UfxkEc7J(8VDYSdw^ckAE1dk4hs6BYGGWPUoH z(do}U5YOy;{W~xg!i{n9#y(rVuorf!iL;I@!GfEst*n|u$ePE7DL*7H3^VcWk6&RT zK-*0iOwX=F0gEW;KIg7A(LWYTd((9C)$}~uCqCu4>-&9hVwvg}!m@%MaPQ$bZO`pW zH)zxxPR~84kaBs}NQ?N($L(@LGbU9MD5841cS^2zEBL9WrEjgKGZuGfx0(P#3kK); z9xa!{LZ2;}VVGA~sKm3!+cCCjIit4@utq%&%|Lzk$pIfiCZGFm@Skx>PwUdYZxmX$ zR%)xJHgH5o)03%lIR07EKXSgYk|jlyoURA|@>t`_dxw5Y?wTeGbQ7~$4i_Ssa`m8u z@yi`mhzlfXM&Qwv@6AL^2=#Dal@|t9>3zflW1GjFdZtK`WVIt5ZiEcxz_ro$w8#DRB z3%$*2xkJT)6~=5itb6y3CX&ZVJLgfzykR}1hAm|jbBTHpk|&Ne0zb?uk6_SbADjfM z(%YOxPw-*?m*@P4!l0(D`NM~o~4I;JfsqklU$zW`uS z(cI0Y2n8@k1y?GOdP*H3faSTJD_;1_Zc>OK{EiFDLB3EgDOuQkhyuBa&z1%88s*Q!rHZEli{HXAZ(4u?AdO;y1hJ zvmXT5G52;lagJh{Sibf>Pe;})=Yu$)LG@0fW8 zZ!H^R6UfE3Y^P~^)C_j;d`%FAA*O{g(?QzOLoWsCMh>Sj`jfk84EF8ayMgy3q46X? za<`2)gaoIZ^{sX8XvrGldoq`sexR2vVo?Y_bZMSO7biaU>N)FKBrwFSd1mJH_o9K~ z(hhaOKlvaBT>4!ugaPBA`yWFYJCMDn+x1Paf2NE2aM{JvVlifpxRB3ehYb(m&*-4oY>;<2wI%jhP_50+25S9cY(5Q z>)c;Qqpbp3hhJ*pJr=8_x8XVF*_`=Xmc~%Lz@rZA4+me!@7jDX7+(=64q3q{#ywZY z*rG^&*S3pWwpt=+IQ|#f_3YKLm_GXEMWg&z1Fo0EXTOf#R_ zZ4$JiU7ty#B5yTnsoDgwvX6eivlu+#EPRi{c2-|5+^YY^KL8i3ht;%r3;!D=7q+BU zh)<3d4w!s5XZk14Hxd#+=O_b>jJ~|5!t%qUN_`$g8XAoLkk~pJg^)b7JN1Zz4XgYx zXDp*Y$n2AV%v-4YJN3p$k{OfQ9i&pLs)nnWB(D7u$eA<;V9C#<7+tGKS9yKXj8VaT zNRKPctuF68ou2NlH1tR+JAVAxk-Q!>zs;(wlFL*jhSlN&GbReK@|V+Zt@|PAg2u;< zC1D2Tg_XJ&fcFM^*N;2fnwguQU`oR-)0(9I;Ha8UM>DQG3LLm;e?UO5!fOrI)Ey=f z)XZsZ4S97Q+Cy~2T;ngdR|wPkn4K!|-nZY%`f)A(ssSLq43_+(b(4?0;nGdMyfWy5MZ z-$95WH=Hk9Dx3*FBuQ_feFZjjBo!8&i}LceP*##Ue?wxhlVi6lqmy3a?S(}Z?%)UD z-~Pn_tBm;}^?mQRab1s}Lr8fJhvpG~nA+Z6Z@?}!-%Zb#)a28d*M+n>8!uh@+&hZf zYg^$<^=P*D$z#Zl@Ws!1VS^ZbCjA;W1ppLO?9YK8Y!-Nv0eiZ78raS@&@qd&*ptQe1O3+p$BDToob;D_;F+sH>|h5t+I#2N1%aHScRUTk|BxhNaU)CkgvvPg-_f zN`L`7CoVhN-nrnhmjR+7j&?DJ0)=9b=>6yzs}KCbDONR%_&>K#&n-&c+0&AXc9PjI zBuWQ>YzH7)d0ac&!@vfn&})+8MQQBVg!6PEyK6XJil$+vZmT zZpq^S(5NU%r3=z3@%s6FuwV<7kQfr(|H<>V$?>ee<4GS!$|NZs+w8S3J3x0)t9w9z zS^ggvoHn}pEo^{Im)wJ2D^E=G98|vbmSxmGbMpL2RW&Laty??x*VXOB-I5#ds*&W| zf{7mtrJ>w}K9o$s;M7H!OnOw`X{)GeV)C(UBMo zo64dva3L_4Ed`>jOS*4T+6E-J5%(VauV0=Ux74O|c$xdQE^6Y-+yO&t|G8VZTyrjB z^nLX_2fhn9yswR6Pcxbj!TyTh+?ehbY_dXvSyJbv`6#$lt*p+UPxp*WH9T$kMy0JQ5| z>}ah-{Nz*K{{ea@`D;@@2>P;ex8L*j63u&C?Yp1Kp+s3sC};$i!_c zU@VzF;tm()P{i?|H4odbIQ$f20l%v=HgONCc@#UrQ9rzhZI_H(kGEShl&=T{xUTl` za`;Ql-C5U`9!$IRdwVHl@z;d$R9!#dAk-`@dtk?FcsO{-Rsebi%}kDKB|>QAt6xk_Eah9s}~+Tgtu8 zXR9~@Iu`fnF0ml+Ai=Ltfar4uJgP=qgGfPptuK6s%p2eiqH4kms3P)FD8ONzcN_27 zuk5mQ7V$un+*&~}>13?mY`8*U4%)uVY>~AgOaAz3QcmEuAD90*djH@58ftgRS~LHr zx=3<<{VEtI65t*UBErLeEW0zHZX9g~HgAtvce!3$TJ1 zIv>dPwkOy~BC>z=0tfW(A{GMHGGXv4kKBSPf$2r=NtOl^9Zo+vqyb*!&SbDEk5+qr zDQm(ZBpbDw_?b&2er+r(uEdB4wfD9PEd_msvDWleN3`5JP)VL0p)u<%orwUpjK`WEej|4C7l65-TJLF@Y6`aqSywQU z4_)NtVDb>?`q1YZfOL<#N)qhu(dCKD@A)OW4iuRCc${(cY-CZ*yuE(bzMbwe^gVC5 zoF;Pr=bB#x_NWCYI4zQgpVJz!xo%7*(lp)LqwM5H=wFguHy(tf`7OiT=KrmlfBjRR zKO7P@|5c(qlY}mu-;IvxXR%4Yv|mvVbLT=)N?5SK?AFG%F`Ea@9g70?9&2F^I;-;E z%J2MrA!!_Xt+Q_Z-6sH8=Y06k00WZGrA}nF1NBK*F0Hn@q68u|=Oo|~w|&csB!sUt zgVwN9ar7w=jdnk0zQE*6Z1U;K8t!*#R8lh!%$NhJb=RAvgV~cZH|3|OQEd79-@`XN zpm{J-x+C{^V9;yWu-ttn37~%7V9l3q#2&g{T#6bv#~{DGgIZ&qCC`-& z>X?3TWnL`*t>QE%Kljw1NLxNOjS-b>6uF_R(X4bffT*M@+~4QsHiTaE`7BDYkOU@| z&b+iiHojH8bc%;?YiQxizRgUYx~lW~%;+IRQOC`AX>(%ju|}64f;aX0QYqmq>(#%^ zru+@gFl=!G9S!r?fIVB_LWA4GNbYOg*!6d6NpnAxf8@^zD|)_m^uW+x0s)`J7zu zQdv_o9WC>_;$p3{(~t^sH$s9QXMtQVm8LtjFF$X}^@jhZiukYJxIc({nG;6=Y5K1) z)|MUw7QK_(6WUR^DuaN@M?>HFQUcA{a&AXX@?KEW#<3p|*qkm8nEwHWoa!V=BZbpS zFaB*?Zbdt=qhha`YB-6#qT8WW+KRGHhF(NBGozjB(c1gv1$165cqENwKNtObHwTg` zGW1y%CxM;BUG1ygSh;Z%JKhW>jpN~DzI%3dw%iWtewq9Z%)NJCyr|e8_lVA~hg76V zH<<8>K4&=o(HxaTzvx=PG4D@w<6C)!@xT)~Yuh|RMZ@WYIvJA7qMCBnPR(%L+49vD z+>dSiRY7Yo1p~{YXw$im+bmp7E+nzdD}{|=OgFCX&~``pY(}4|kE=?@?eAdU#o{I~ zm3U9N#B*m(S!g>}0Jxq=f6RSjp>%pjNtR2wK6=tP8p!gF*Zs+` zL*Mxx%?B5X9V=eiv}WM%1%G%IzE#R?S1SWfJ8k=!wLXV@fEbk48%boY@hT9h`zYAv z@RYvPjE&z}{f}Poe|1+}rF(Ff%%qLlbnSY{ty#okNIE*lUp{rPeyNo`Jcl7&p(}sD zSeSwVB(&AI(=dg!&l0O>o5zk<2SM6EJ0%wZG9OL%*y)Gd?E~sY@)1S{)a$Gf2skKt zgKRkNttL-jQ4(jxXiHZmSK3@0D=wobGXH_`Ib{ zFWA_)0J{$672&?{w&yt8#iJLG-;UyOefs7P=$k)@o1}*ik4oh|CgpV4bXr@( zm`f}vzr4<1TnjtA*LThtWp0@Kil@vu633uE)bsyi?!BX`JfEmhj4csS6BS}5DvC<8 z0b&75EP$ZWJE%0JDF_PEENCK8r1xS0q&ERUss@p+ROu?ciPAgYo&y+@-?#4l>#lXr zT2X@Vp7%WOJTrUt%cpu6f-^#R%M`;GWJ@?mNg88#<6cO2mUT1_L+`bzpRG_eBe zVtH-i5;87tZ{_M9JL()zk>lK$CSiQpZ(WZfq$gOR6IPftRE4| zS^4pvf^<$aFmY@D?Zxc(fiAbE8l*xUEsHt>5LA&(238Me0P9g#U(WTck!27rKxg-# zE6+i10GFcPg}eP$T~+*0))p&PP*<YbW>bXw8bl&k}Bj_6O98%jvHu< zvn4Jj<~lli88fx#v~}q;u=oqEAC~M=B@IN3rVFJNE$^+BK&MU&+D~q-4nqK=^Z3Bz z#TG?Kh0shl71pU@`!oh^C1}n{34kZ9c!Zovv2HTZzeP9!Zt$cqrxGp+Zl1co&RQYG z=F4tz1a!tJeS8<{sQLy+gt5ukG}aD#uv| zxDH7pw4sVTx592fkt5=wlWn9Bnrl1~{fUZA^?WuRW(tGywdl?Elj#)yt^6 zcKz%Su(FYpF$p*@0~2nu(UwZ}Z-f^*x`qlTfyzs9iqu>T`5eRnyRdkzX!bo}WfxF5 z2mo%u%(Yt3L#q)G>$ky>5&Qv>nkVZZ2>yo>M;b`PtGLT@I}woBu^Yf)x5>@9gxNIM z$c|KM!5c#DbMF2~i4!MqQ0r-^ZArZwM~ONBt${-G6sCFVoJ)~HCe4k%E_`TDIZakp zmc}eGR2I-faPodO=Ux;V4&ar*4}Ono45%MmXSW`jg__$jEE`K~b|(Q1%<|{-SOfdQ zv<~--0e8@mXGE}(^gKRK7BEO`w~9R6qW!GD3V6hWM&A zvbSJ53P%Rek)$RICD<(VLB`xdg{i&IV*xWW;h?xPHNrI#yGY>ko;2zPA3=l|y9GHi@54!E{g7bCAARBvy{|9}{eB)h*x4oi5B zhFR=EQt?2$%_@C^cqJAPukbJr!9Fg0E~5ZCfxr$%mEh~&G3pQNA)mPq;l!;A6OeZB z7A;=cJ`c8S*uGAPQ_=?~?s4MSIjy#nAb6p(OMwMFnwbtoAn8dw5hSbF%5>zo&_T-l zV3ntZ768S0N^6CTf9G>(p!01()6sF^zzToTpv~AYl5-N3$0>UEQOwpEOZxrKUvQL> zRM5KP(?|CPz^z5zLmU>hLR$P7Sk~!qG}=}GVa4`FVfu9ub4D{VMM)``#T}vG3}| zwU%Epp6Br4H6z3mi>sgi4c-^vV6EF_TBPa&hHBL(}i(qCHPb zBUn?uzFsB1T<%%yoi4gi<&}CX`w7)AH`&L2n;fs7RBcZ<|F_c|kI04SaPmZJ6z{;8 zgUwjOzAqWG#dn#ZE@GU=lc+h~U%}UJknj{`HW4v!y?M zkY`b$Vj_W z=`Gxw^zrs8V+?S(kC=I1{*pPr1-ZCUIX{hGoVl~6ywzOc^S|hnoe{D?gb)MP)zO5N+Y14>w;<3Xgqxg+uH1vs?9@E>|{6)qno+Hp|4St2bE$ z-mog`$Go{%*?em~=Z%V4Oj9r0J+C+&jyssuiq+1;cMgdD_+pNNq+xPfkc#EktUS{~ zG$=NOsgGYss5X3|ACtv;9_UA)Qqx&`?*8z*7xeUc!B4fIF{&tia=aUXP5|)b{Ej7| zAQ3B(Ud7d#RRt}srfWqfTjb_pBH zJ-%Ft3rvb6&4r`%kng)U@k{!V@t?0MDk^?-$V{xW=+KYN*%X+jDhxhUCLT;w-;;XP}4#SV*<}JQg3hAGJKZmN0U{vaW5-^${ zE%FKheFgim3F_w_sMPVdY~oVSiG1m6{_NvnE6s3U|di@NDdFc&uT#N;~}o+6vb&hBcFe|E0tLM$ZJ94+P!A_1E_&*T$>IX zNcx=IQH)=cs&7ApN8yb5(}*v7CO+I;+ykE*42)W7kXY-LVhx8l++}3rjXY7{+Gkh| z5B^O;R<`VyNx?Scg7y=Gxq@k5?>UKFId*TQE7nv1GAovpH>25Y*@S_joSYlhd=pA< zVwl&B&EtK+?LH~TzWwa)ei;228}GI$!g{)<4pecak2UexPZ&Qa8@Dpl+Kx8H)mDA) z?8b2?0>{>v%FyGK5UhQc)x1K)+7gZ+{qI?sd3jEjikTvl!$m%pJvsJcXPspE!(o`i zv=Kc~rdxi0&85G8ZUYH8P8DQzL_d2Z9Z=}pIU;3pRUed{@el?D^H z?XNP;V_&2aFD%G{e?GMk&3}10D5v)Lc3W~nrzd@7? z+EqL;!>EN7@Pzr$_OblPto|m*+t910r>9Jq!$tNLYV19jA!>nrC^e@$xc!c2H}TcM zW|xw*s;aLDzBF$S33e)-J7?|Xw+Dx#CP$+5EjU;Kmm@`e4@I3!i@L% zhZ`GLUc-#t@M!&{A3XxhZoUDbwG$PN@E4zxxA5^5ZSvN40w@UtT&&cOE1>7_1ZT7; zUh9e%Pvy2(35IKKu1KFSef;s&e0EF54qeV$dRa7exY^qr`>+J5GVtd4K#Qmqf`%$U zuj!9ECWo>`w2Sr+s(czo(Z_%3$$i_wlq-GOj&2^0j>s30Pm27js;Zz$!OfTQ<&YYj z*I2Lrq<)`}%_|nc#(uZ0>n^@xi7S&+)^b-hQw+J0CNE2+V0g7enU8@$d^xmt?%uuYRGSU_+e4lZ z=(mU~D#ZzSKu~ECBWb~)2t=Lm8TOOutcno9oKP=`Ymr+^ZSf$pp>|jS{Vy}m!u}$giJh=Kqlxax2_w6)9~|uY z(spOBy`~=*HQMNWvS)Ilelic=^Qcg}#-kUNYL0(p9R&_qO>5jVAu7y0opD8=$Tx|`Hn}Nl!=FpQ3CAIRyCokH?#EIz-&!iUIe8+! zLQT2d<}rL7&$aH*z#9Lpnb5m3k4PTGjU4LHXm6DY6&SkZ>> z73XjH`Pgbl|M5bJuyP5M?nZpDT6d!=JCDE4K?MysiU` zK}9aUbp(DO2wF9PhO1^C{^o~8cT@*-D|l>ne?d5zi|C=7K zf#!&uy`|D8U7t80>uSmtnYa|P7!<_@XOe z>=Rr1=FkLWm&16brR#eqn)qzaJO1VgN8n?eyM@AmqReJR6HJ1OP~7%2Z}waWsl1Sz z`557>e>5I*;rY-fdy^6gL7x8IAlM6`LhLtt(eB^$B9pZ32hh;cnSvi6&&+V%_XO({ zKE+T>1>YW!8AAr`B+i?2bFr|(ZcsIeH5dRsk7}!Z9dlk0V3kRoc}o+*E1xM>L2Oq- zbu-+5?(0=XHR0EA&eWjPo!y){e{%YHkQap~F#3AU8;=Sz=1!dcmOB7rTcz&xBScf9 z7f;=MeXs zTKeEw2;R$GP96Ml<}n|5zY$;BMvL1zd7*lOVIU{0oqpPI^LdogLzI6wF2$BVn#&cn z?9c`b#d#t@<0%m$`D2mswv#+-_sr*B#in}sv>|@u(S;(R7N4;%cSQ7Kl57Uz{G0CY z{jBR41B%!9a&!O+pdSYTQUFXY(Oml1&`$aihZ%2!>Hqq`rIDgGE znM@_-c61S@0iJ0A1QinI{ks7Nnr*ikt*-B(?Mj%^zSij6It?7icZtx)J%Ost>M#mr zeMzwntm+%Wh^*sQ$>EkQd+T$s7^%Nc-nRm>H^Cw$DDWL)x4Ik}8d{7-*o_lc`my9r z1lLZAj9*@RtuuyobicGsI+w-!ro{B|E~H7%pH^Xdmp%!fX34NK{yTd*o-iC)t`rVzpMJS`U}6K$;P=p# z2YxcZI#$Aj#B1ke98$s=O1@X>g!Xo)n0#D3j10#QjASERS{_`x8^UK3qTU6^VQAqw z;=X=NzU6w{EsKi^P4^jZ*1 zVO(hgXhB{!m5T?Ro&ctVgocWN1C6cJ_SOPI%DcVF&;yCOR%(B_IGi29S-LK{1)OI* z#BdRaJYv6`cn&PAnZ4$U%IDr#{WTXeW>I3wWh zf6@jwN&u^OFnw}AkYx@)ix>ZEMIB#P_K7cd&etj}+8cfX{aLCq8RxKQ*n5?qqP_wm zwYW%2ikh05hPa>g)RT_jmPSmPM$UBYoW1*X(W7jHA~IfurSmqLYxf zZ>Eq}0v?BTRWE>PE{s;CcvNXEF#tHpC8E?|S35@=p-L0@IeeJ!a;8{##;=vH0ou#rykC z0cI$*yM0S>f7$!>UEB`Ko7m--U$XSu#0!uwfNx=|iUk*}#cb{N{Up;K~ln|@qQ3#ZVQf3i5VRD!;yR~U!Qz4=TB z?5hCyd*Y+4c+^L$D#U6RME^zkUTc-siU}F^s}yuw@U(gQW4V`tHn|p}tg#Ut9@RBg zgSEp0iRr#T&F;y*tn!MAp6U}l?tz9km;VkciNn^?hJTT8B7%SbDxpm_b<6GGP7A4g zh0-HmNrRz9+1+m;Oe-o2pptQD?%% zAX+p5bB>fv0IT4zL)?;P>R(N{X{-ZMrYH>U$@d4 zL>GUZZ2N(}!pm*BjRD}xfw-ydi44QUb0D@{Nv|n1EK&?^uox3eXs&x)npd(Ov8o3M zWIG5$MC@?J_NQba351sktlR}lQiAES zkWf9qJ?c;nliU%hJX8048A$@TebJ#_rcHyvAj>Gv(}K}Td{Wt$Kkms}Q&!_6rP^_NOh3JTE*e7r!j+%3V4 zFpcF*uYV!32A;$Zz=aoMwPIRYT7<)3lo#z5CWlvGC5Y1#LRb^8&6TRg3YZ`>43|0R zE^3@HYyPgV3AXg1tgTC*r>^f6p6`+i!GODws^#iKj7LL%gzvL>Rv%KzLtnf2FhZeM zAdo%ddGcX{DH&QBfLuyKer5vj_XHqZCNJ)9vLeL1Mwk%8(%h2Q$eo3+uv&Zd!}YOF z9T6^5F7YSm6JD`P_f#Pgx^T4k1{%&6dj#VVYI2e8`FrWfaF>UQ%a<<~0)<3KJ`gMk z9!8}A%2C>Ok`?9U&0qleOsm6Kx=k{A2Cx*Yvht@-J7R+-mv+rq7!%hY&St{GU=K$1 z(?>4_8vcaOqLHM>Pn1NUA%4u(HM_`qAdaj?qFnXrM>`=<1meXF=%Ct(prvBo+jP-sSpqt`Xca^s!V#>W#tzepwiy|tA>6^Ia*RYoZ>r4ih6$hOE3iSrzGm>hD@ z2TzE1mfMZ)!9q%g85`I&s~t9IhAeC~-28Bld%6#Qg-Tp0505LctU1V$>%;XZb3n^U zU};2@TuRZbyjOs;9>~$+bCMDJF4|34A#P2*TwZ_4*|74K8h!R~$y=)U`z<%YD72}S zYu3WsI%DU$JgY#XdLu})0x(m9 z)szdjA)Yo;78L|k--bdUcBEMCFX79GhlJ$UZp#0mV=q=kSONjkmq;Y_;avIHELLUB zd;H~_`+tdl3fv$F0Cy~IV*C`I7jAGDNDv;=(XcgmlChT&v77I5Me4?WMjlzXCD=!* z-E1aEF+Ql=uP=G=4P7^|;t!vG2!>V1&(kUdxJKhICkPmM8vbov?p92^Ac74!-Xvt8 z?$s$6q{v4Q@!mi1e#73*=)=H)s_lUa%`+*FZaF`|c^;U!I54lS$>U{CgF+gLyOHOa z;Qjd(uOKreC(UE}V*0U7{l!I$78BF5E|BXdVP)RoXq7rQF;bE4mRx&;5`fQ`#~JI7 zvMInrsyN%==ITm>OOAY6k1P6Kx$w5iJU22F%pp*)b@9G02PmNeC!U=3I^yZmH@HDJ zh_eLJqeCdbF+#HK)poMQkMcO6=-x8Qs+hm9& zS?Gi9>>cRK)>ggp{>&W$-x2jz8=I{b>24ussD|ybse>pNup(fXzwJZ%l;PVLMtSNP z!DA#&hOs2)p41*1hDXO>gNYh}wCfub2YbP21t%m35ZMX>ooxY2*wd%Sp~f8B7UZps ztWW~90|O?bX4-}xPC}4U^}ldCBKg=BmC_@|K*kO1lWzdhrLV35p?>}$ILm6NJPCy0 zT41D6JBcfi+#3jh^n-Q4?_|j6rl&-~e7MQI7n`JsHa9Ln^eK0rA1ZHd2*s>*6#~`z zagu>)N$QLGal?>m)eES0oCML2I4rHYN%7q^O)a#1?>iG2 z?G!dCGCOLhTaU{R+~i~~SQsy>T5vNy{u6z{frkF;ulmns)j_2UyD_a;Qx&n5QB6r! zoie|vKW&%aO|OGL3D8AD1y86U@IJR`7|+?JALl5qu+Y@vWLhX^O@3dru0n2~$Y^De zTWj(v!{_TL&$O&By<0T8I(j#HD7ZomgMH1mnudLUbtbOWHkt6h2i_64x=c0^sK7m0 zmdTd&9mt0<ZfodT}vu!tiK}L?B4Ym9@d*)(qdv2dxd%B&K{$K1U zBTYWCO2}B#nI8cI2+!JT!7Tn}3>mA`Jc**}tPPDadz}5pH2;hw zQdYcHm`VIYnY=m{4v=;8KOa70LGx>OLq7e*V6z3&ER~VFoJjBBc#&W>hkC*XfL0o8$@Xdj8dZ96bBDrck=#YI2j8Jhn_eF8q(crBrP?g z1wIW1MP0@h5nDsn3>q9nY>UC%-&HWcl)>z`C!pSCcG?uENR>X!cQ1u?pptNRStun5 z!VC&PX83{Zc$)Q^Shc&M)fd7d#8UAid8Oo3D1j#Tdb+>hr`K}=hA9sqj|`k;6tf*% zvWyJ!*6-c`*M3$WXo&CF1`1yJTvLN&b{HhIPq3dwXl4>}eicMv5H(#=1z)GaI>JeN z6MnB5z-fm@hV8zdHyg+yQ~t@Dkg##~n^{j8=~`l~nKF5?Qb_XL$F+sr)hy1NVG9MW zOn*N)Z@_Nxziz~SQ|B*OwT*)>nfV&s)5o;PeI44sm5@EoLN zYEc?I-M(=alLBwKib=t0<2UPsQ2*+IqiXTg@yj8i2VF;$K-+oYW8iT{=X$_%TZ2b! zd3a~7QsXcpe@pbMMT~+iN?{lNY%ymnDmnWQ_iv$`?oz{!W9se(z>l{)Z@8})YX~Mq z2shO8_V8jY6hqY?B~2hA@GBB~_sp;{asX9UmVr zQ9jYgDI!X46Klu!P9^KMD)}=D#eXe|+;KgZWU?kV7|S^%YT@$A%HCjqgozr8Ye83q z9HP8ecs1TgER`ES8GeHZcl=1&(||k+VhNxql=^!w;=kUJGL)ON1Jzc3|0cG}od{0a zx|WiE(pK-qCU3;UEClNEHdT#T#}4k3t3MXM6TM*N4$b4BK<;U)U+Ij=)<$8aAXyu^ z0;9ql9tPf@@q_Cp*Y=UG8|=L`v|H^7~^3r<8Q_`Q)}0vht7D(X%Mx%(0|{fgRL9! zos4$Pds2e<;y^FX90)%0I;qh~+_+Gvh1)CzuBrJ3Gw)`L6mSEf)1R>w>!{eioz|af zhUTaRu_>FN#H0xeK5{RSnD&3n&UU#E#-3pDrqlcJ8Pcz3?-jrh`WqvZ1|}q!Jv5=B zE}^kU*;Qy!5TE{V$azZEH{f`@^#sW&W49~Z59*pw#oe{yjNridO!P;_rzzj|Qv|lQ zP}>t{CH)lCIa(SGAzLs;O*9HU33oIfQ;wEc?UMX?8@cR?O)M2r?>TF%X*^UFzHGyPZmzAbLVl za6O=AVMaKxl`SPQ4e|LQy-i!mF`TXa-Rr=HscrK2{XOkZeJ&S^1{!V4BJDI3dqT}Ce!r%QS6Vt9Ew6YmupHW{buwzqGC|*lh-;|ldjxx4>ww=A`4Q&Ie z{0+pQ9O7=VW#$^Tt{KQx#A+!H9>_s61)Tfb)lf;JN=h+I$|-*msrZXhGK-80XW`fL z9yoe4FWB;}hURo6r+!`mN$T_3%&>YT%V zDP;19$MJYu1>zQC=4)18(=y;5`BuNrAL#cXRy2qJUhS)*IBipJU7Lkxk$)L03 zp7Wl6*?>BEf`b-H6Y+LTl;dK?iyA%noWGvj)Apnjy)=Po-#9F~oUQ8(hWD1OQPPWW zMUo&0f1+nUPkOCs>FG*TuEedb;m6))P^}EjSiwN(JObM2lQem~LdRvKoG$Y1CAX51 z&dDu>iBOHthoVMEHkBBxQJ!-fp{3SvOh7(WgWPerZZNH4eFH4ZR1yD)TPc6L5`DqN zGWAFtp*+V(a#8n)`}-V~D&3R~bI?$9s-2LXZI%);y?`#{4@s3%>$iK(WzIoyWy{? zQUmR%Z!cTd`-}xFD|Jun3#7aUh%v7=}+{Q zrk3(hB>DxKRC8$U)3!$4Ywo9K$f?-CzG+rEktpq z(0_8Qr3M~Z5rtusH|y>^0q$lb^m_P_x1R`()gXG~aAdJXV5BI=CN6)9Tc*3u*s9So zYyPr?$%`MBx4nz3mimkKb#%|qv;qG0dixS6eO)6k8rTpXq-BvcgX@;^Y7P zi-IY`&-~B#1G4_V`@ZfO^o?zvMhPX!r7Zt__wcQhcdSxR-ap-7HpRP>+hfYBUCDsS z|N0#b+3oOj6q=*08uAidli`M$Y?~zi9q91A1I#Og&ucy&rQZ&l&Da$5<-cCfe*ORd z_j#Ti8}*%u6#x6ob=PrrSz1~=>lmoC9z?(-+sH7k_@}1xuMeCNsyWp7Gs_`9f;w`X z495dEE;ZTi#gP+tt}f40c1^LKcaK!!zo0l8j&{ua>?!z8D0pn_Z2QUDr@@YZ?3`ITF!14+z8uKwyXcyE#Ye4Ar5Sq6${tw+<&ZY zTWn^GH9lojsPV(9qC02nta>Cd`ETxQ@~@RbVCgL1Y&sR1xa_&uevCT7%6?h#O5~30 zuCj;Glfw^A{PPPMWfXs<66rhNmg?@?^yE#4vnBaD2is-R-UsAyc<$NRmT>r0M&3jj ze_+~on?XZpH)8gJNqRelHPcU7_vPE|B_-(=mS@uCgL`VMawY8}{P*l!v1?shs#cHp zhOH%0p}b#WZThcXC+cka6@7P=u=(#q5WG+O+EhJb5e$e9_wbWE?~==&96Hu3)sl8^ z&~uJlF6!OiF)%7C8-zFEIKRZ}yJZZwFtltNm^b>0^B%Y9)SjfI8ZXa*7TYv{Dmix+*uZ~Z#;A>oATZZN;^UYDnrhG zZRigR)nD>o8;IVDZXmi&^pA(LPZTlk7av_JX>|su(BG#V zTSm)j^H*;;M582|+T>-E-X}Z8o6;xHw@jhc!}P?g^1l0Z`jTggAY#h+ zfpwwZzPQj=XFKLe91kiyVHT=OzwC56-hR>FID>O&JS>~}yUl4U`@6%yURMSdwsm3< zD@;sZLLQzA3byGV;xIkvlA5qZU95UhDiztq&-GXCT_z(^LN_e_8A@PXU+ zJ^d!i8k{-CL-#zX5NKj9JK?LycDYh}{Pj)vH_e}~d*tYR^mJcy+S6Lre=eg8@!bW( z^QR3VER{=y#bd^V_lTHk@6bJ@iUXQ?Rf3qs?j7|95BDwsY1OEl3+M=Kzws*YR`9iJ~mkskz z|Bt(%JIoN9&J&}nR_@WZ7?F24>C_k2{y?qTg->QDZf-Z-+&pRPx~J!Jo>m3T-rkX| zA7@=`T5>!4nt`;RHS-)b-0{D-Zurj=Gaoo&gVC>^9QYuNO}j_rf)YM%#a(!D?*)IK z5N1y9Ol^8pG#D0oB1+Qqp*MXPQ|%D>?;H``Wq|q2^#V6Uu`9k8hAkgz>6p#^LXJaK zP<3p#w2rl${E4?srJgzX@D)~i-O1K#ZO&L4VrHy8@VmQn`=8_s!Eo=igK*|Z-QyaJ z=gU$k$Vt09sG~jG2aY>ctK^}F`unF)a+hVhS_!`#;?7JrHucgCTBV-GL(C~!6S*If zN6xq)I<oD(lj_ zm1e0$?S;P?_p5#HW5PMwZ}N7q`0q0Q=@>1Lg87U|t@_{PsF<(tS?1l)?#Dr8 zbI%FL3Yu!;F?#pgGbj7(2s@)1WsqB9bUlL+9{#y;Ti5GT5kn*XQX`AM)rKBV|J%pQ z!{GB$wN!UEefrBwj!mOdrnc0xfVl`4GR!aPkeBsT^KCyQG%8=AS^7*WYW+XgqpuD$ z%vgfmhNwyI)>w7fJ8;V>(IO06owBS@hkYPG2NRE z5aB(vzaO3p<`jkhIhh`s`!eZt3E5_H+ox=gJ-u-Od9T}@G&K!si_RX}eymJj%^#IO zN~;s)RX;R!9e(X@A;j=w|6G6NM#ht3QfN|D99KNhh`qU>`f<>5HVq-hp1*X^{pO}+ z?0$+O+JHU%R%X=?cfj^IMenCrYlgTl`{zpZl}5}h)tBvt){H_rlS0A>)&`hL)QP~1 zo_BJ_o3jizWT5(+mb59Jk(~mo^xoJvNgbouUOESTfkaLMyGil!{QJTg>wkK2t4uYF zL2xlecM?oODEPJx9H!S@9^YLUy?WQf$h1MXdlhAOq&7)ZEb@BrOf{;FF0XXDLXVz) zcL^bzhMfrSQheM0^Dc+*%)%3zr%<6nbL7~r)*YgSW4kzv+w+s7a|AlrU+#dcXJG$AswV8lMk zoALZ2;T7(Cxt;JV>#Gsn5$pIR-`aoPl|^gz?iWdo3R)8Iox^dd`+p5sZ=J|uzPYw5 z{!)VlZ*FcXwUZWDuA#_&xiWrz8*HZ|s4#rh&hyC$*7=h)k!=(9ZRE@uKDOrKOG%*= zJ@cIMRsVcOh4GYWn#L!0q0d>hx^X#@GVn2-1MWvw8LA$A2Ae=2M~9` z3#9krK>bkMb5OISRBN0^K*7PKP7MR0%srjz`-RCn#W((Q&y25Frqf8dE%I^>eERjk zt1I|&%|{Z=XSzVS^}vR9q5fSNqJnBs}9H|tv>yW`#m6htm(IKA}HqVE9%SChK3A7!mHs?v$6U z{4nvK%_t@@?TW>I{TAZdW&BtKw(bwbnmmr+bswHv(*GNq5a$vbaNC)tTXw?pKE$){a!I3lGQ(dWqW% z)W1+O5|DSqPRhFOjrJ`aUf$MNwe-+MEam^XR=^Z@17awZ0bcN|TWr_5o{zIGIp~gv z$$Gg`#vlgn=Nx?d?CvAwpljXyos(Lzdz?iJko zbo~vnzm$B~nHSZIOwTv~rH*+a=)K(Jy%g_et3B=^VJEf8be5a64y&NH)o(i6&#is? zI5t|Ho#|Ual8K319hAVEdwjKO?^zijKK%Z)+vkb^2-RUYKj9Z;4v=9Mp)st*ZPh*TKRSOV&2Gy zDctzybi4{Q)dI{F4KB#*jY#FPUyuw3h?p8r;ZJfC17h3{=Mi=>mNB?=4rpHGH=S{h ze#%3*=coz)qkPZ(6CVy7LTLO@09Mk67rw@?)Jyb4HpBcIVJy=4KypLv=^Xdo2K+Z z=Nz4b?gr&XDG9>+;@+Ri4cY0vHcl9T;XonX`IG8Ju0n=Uiy&U0&htOX=)lZM-vbc+ z25h~<*%g(M{8()UT4qyE)VQiGO-sF8u=&P8cZq%>0^_)N$KmZZCV%U6$gnT_&f00E zqq%2%^MY^aOGnOBH`bccf~+~m1q54TJ%ByR4Y~ouJPLjy25JHTdPb zN=Dxv8dHE)pKe9OR0A9Lm1%G4`qEMGuM<*iHSYmmj_P&1TI;>wHn)Jw4$-3m8(d3`HLBE0u=sqcIyD zui>Q;_-q+s9~aM8a}-&9^@y7A$3N#~GRTiG<|%d|V}U`dM;maiu&A-N?46@}YzwdH zbL#?R%>9Ae`^Ui5{1Mp1XPlXD>mYsK_iLQ>=d-hgyp#QlDtIN+PeK!_f2d~rlc1dK z(+1^~96-&~dq@b9K`#{iHbuaG3#7X8)+;YpMui?`upr52Z>|#|_qkaME4clkOV)jC zGgYe?D}m*&lQayZ&tWO^FI_8}t5%0y5w~{Q4{7xG!wF$pE}@iX#+JEu&wQvbV20cG zef>anf%P$tF)jG%_G$|LcsfBpaCq-+F7=ccO!_dsOF%94Y`0bEvnLJB{&Q~+L`9yr zxHx4I8u5d}Q=0<+ZyPidu8+gk01Us1I_(VGvrB1jY{GMmHwTvrGkfcYn*p1_|NOwg zNcb*&^vvp0>>MvGQa@oZK6dOf&q=IUoVC@={Z3n+5~H|{aA0>C&f>4>eAzx?)vk5t zU#ML=P7biWPc@J$>ymi9xVwFTZa? zAye-VuwrHh`hZEB<_rk4XO-XclEb1zX9peH;=PKZu;Giqo4l)5b@vX|$#j)^`WMt| zj~CtdS1kKj68{g;q$>kWmsOpM$;kdM%da(UQ#m@<|FYi!(n(9M1RrCb>ZY7GCp(n3 zw#EwU#=hU~y)p`I846c*rqD1Tef~AiJ#lU3|5>2B>!veN%}}bJgtX*p+{2KP=Gkip#tGQsyZPSSU{vgY2s#1CUxrM$mBt8@XTP3YK|F7ya_S6PHP zP-YO{99vhH#ZMUzjN6k+)R_}Bb}&`&WC~DJas{t#TXw2rDqmvShHPgFo5I~3DPOi~ zVE2V8oion&$^1V19+gt2bag{W(DE zRbD>F6z%kN7C(Jr+l?mZqzlIA8%=UMJz>ni&15JFnbVO%>~rp+jI)&eT0ZQ(xrxJ| zf(uB{gDJ21mPGk{g#^X)wtzf0zZcduPk1w8J%nG#EkjzQCHHDOgSbKJh$Tic!ZuTD z+C}nP>t^`m1Mg-Mkc{?-e_RmTis<(WZ<|nP_r|o309u{YH|rZ?h|3(e+>4Piw)Pf` z;$;@y%a>R@!4pvzv1QM`xPp;;WNOn$tx)A+&5O0>JJR=dcyyvAs1h;;#`)W`H6e>@ z?_<+DEWM*M&AZpTeryxer>>G@LDoR>(^0Gw__Z|)9K@~OZu-tN@Z|xMXS`F4eMY<9 zwh`P*CjFRM2GJpu3N{;OG_K|)JK(^sBI~4-JR7=R9c)p zf06#h@r=*4CKwKD#LbO%zR$Q$gM2P^$E*asJ1trd9dyUZMkjHAlr&n1!0h+N9RM`p zW*=_JAM2mkyQ9ff$GCR1n~K%c9!5hFN9soX{VjMCCqCV}ttW6<7gCsBMwSh$l0MvE z-eJ}}vO?B7v(?l}ks!IM0)KbLhHA`sN1o{@0#Jm3Lu&OYLAA2_6OB>{we}V!#qWp z()L%m;Yn%=V3yOk`*Q0EQN?HYyhwGo zko?IsYj-ilBOvd|=h;1?v1W?qH=}dVF`+Z`CHcV{bdqpFNlDbn^33DAD*bmv0z1l0 z+1DFE43hc`Xr5J+F=$cH=D+Oq@Q9k~syT;o{1g|O-&LKGmlPmFE}DOqL0iXHj0u#~ zt;Q5jaw86#C_;<2CnkM3A-baZ@H2Xy3efD%{Lp2U&5n(twRh?;U0bP$d4@?x{pCmlieqRx+_S>W{Ga}H5# z)Nh=ILKj*xn+pW%^U<%>Lk*2+Q!-p~HgV5$v~wF{jIio{NQt0-|dnw&*ml1IlI8&=7(a&2H<#7gBsIOyL74|H@7ECro$bPziV{y&Ru) zoVd)dAv;Bwll8d|>=3ORc@n9iR3H-f*=Nw1Lptb04q8NwS&VK zEDTdW9%_ILr;unYrqP@I*~3+L3lH8*6sh*xE0~VPOPZ%jHdm4bcLFZ|SlOm?{@2!v zQeU-Bt!agX(^!Z4WG)(lRiCceZ*ke$r23U;`=KppKX>C$#LOe>%0yvZlioq~oFSWg z;^_Ohk6CsO8qTM7$kGUdbnCXN7LP|actz3&fOb~RnW?E{w11#yh)LW2PAErIqPHj( zzM&aSqBMG7f=)Eo79z}VhdlSu?vh4%P`Iy;IcwdKM zG_k6@8xA>;jT5h8W(vPASv5Q~ZVixEDtJ%m|#HOjCvMWil^ zHL=!;irR%9j->B`)5z)W%+aXJOZoiHlCnK7yi zuh4>ZwqPIxvPEZ(L^U(XtwE_S9K)Cvb$W8Jh>~4ZE+?DGhAfVp)5q4h!h`RkVP#_) zHbFml0fqAMiQa6_0nrXD_sp@xkP4{H z9zz5MF`4&i?gB#u^6hf&-g9sE$OUdYMU>e7i2pCAdFxpUduCWHaw4CI#UMG{X$Px-Q??wA4>`i6sG_=w#ig)=$7MaVy=k zsD;{X%bI%=RV3~3#f~*vy{5fkh~kJ_G$N=2JYwfXVl7STH3CH^j}Juc+3Zkz*&5Be z47m{?D$@73IkHr8ub_HW_tVP}$IZseij4pM`IR(UelgO|dVwayzr5qr{4lBF?(T2- zo2#Cx#}iA({lJon$X_LB@Soa83;C?S+B1g!OZ(B`gFz01qmsVf+d0=d3#zkIr`e@#48HukDnxP?A^mSbdsSf-bGAiot{V)JZdMXsBNt21@ zUO6f?Zn6|(7UX4%GU1e${G_}fjhRfSGrEt+x#gm-UH-ix1P4T~DQ@$*g3edFCB=w> zTo8IHpm}y0=Vn}m8XgB0Foo@40MCr}6Vsr+=36ZS)vHsxg<})U!BobpItcGsW$^5D zB*y0GzOktapw!n>Ho2Rd2zbGPx*q1rk;oS`{M!ig%*TOAPL?ax%)ew-j=yc#p%)kF zUy$BrM)P+ROsSu~5-Khw=&bCt_+X=*TSfC)75NQ=b&eQp=8Mf9`SM1f!^TX6_9soQ zW9T|y+n@u6R4gY)>x)_km2~pM8*PkIdB&|TTGD(n^#zj93iCO-Pmt?p-fB5fXR8Dn z40oRW`wtbfN-fCsSUd5TD?E;tL^}crLeT=z;^*eEEo#xq-BqM^U#Zk`Z_1dx_(Vr}*nq!Q zDu$)W4|S@W6!2rZO^;QPnn4NCVYX$fX|>B6%;o#wM#7>+pxi)1WA-?)M19f=-N3Ae z_&U~LevK5`(0nZ2uB>zx4dh(R-4@7}&z?A+cv*w}Yp>u#gwLQB2Ir1Rjm_x3lfak|YjQB{Cdl{bm{A0UN;X4PK^F*%%d3q)j4u7^y1zgobNiq>i(W)E@{M` zftE-jLY=oat*=;|)X;VSVWnVvX9X0n;v&-#PmQ5^!8h1HRWO$`2pf?9k?}Q`3!@Z4 zh2$q%H+k#P)c6;~6_l@hPI!aD2ZeM&Pxc!k~l!=4Ibi9pyl$lvdCA_gd6%hTfi$nIf=4CK)hI zJu{Dz%md9x+qEykD9XGu?#MXOSF*hDnVw!JZ5`OO7@SZE8r?}?VRJtZUvZRmf7rai z3Kw{Hl4}qJ#%AbA;XJUnO&1zTdJnN#?y?moZ%7S|?wrO3smGfqF?@7MzC}lbJNwv& z`Op|jbEa)R3}_yU(HD0tIL#6-sD_kLjM zU5NSq2oZA;yl)W(q}>tD_e1|L>rg7|i-G=lV(!3)Qb*PlVTHU&_9a598^&fiME5|7 zh>!$L#669KdKb$8N$F(WAI{%d#DM>2YUcK%y`KbGN`POFjzU`cA|YIO2~7%f1j8X* zpp6z^wM`vgAa)_3Sb_mBAE+qO82v~4quvb!GS*zFair-;9JPG{IZhnf^hwr15j)L2 z$7&-DAJLX=2?_SgCGSOZj%dWCA%&PX*%k-;9|qFM7p4eYF{-=^!;2Dl_-;?#aUeNm zxg_dd>;4N35DGDTaG$ z!K6qIhV4r_twkV8#1Ml40%jm_Fd;03=`i#ayDw^Bq|F~QmV4pkuFxp2j2!Er=Mz%h zz#0;0-iecFJKGfG_31uShIJkII%8Z}X!{hW#;A2j z0HwA0*X;6K#+JVuVu@qj5s4c`(=N4Mm$ln$U5uvbR&t=`44OeO|9dlyz zw__T)y45NbS_K43AuCH$Czg&3;21PA~@0t=>9>$?69X@7siA z_}zN;UdZe{S;2TPNEghyFfil3 z`xv<}K$aCb@8BQe7PT!r1tz+Xn4qMLZNUYrv9H;^G_Osq`bd<>B~gsA>)mhB_l6YV z2+dK(=tT3iLZANFpwxJYJF6jP7n=T{M!H`3cNDyd*O;-H2D^570UtE1e z$_&#@FTcX11$BHb9d$~+SWa?ZP%#njzdA)4Fn=difFfr+%TyEZ4pFJ``fL1qTVq42 zL~8bKX7l^1UfFAV6ABqMZyPym zz%CnD|LOL3u6GAHq+c@?DX$nsLoy)St#%&!$mY9X+5O{*#M#AkLS@Y8vdCWMp3ok%pG z4I>p!PcYmgP60Pm+A&5#!Bc%!jR^xYk3mJVwgwC+#n1Xjk7!& z;qTQoHQH_v`L!COH}?AnhqFKT(!R$AS#Av)sMa(I0yfSZ_(GmEPzV9mPYfikQmH_8 z1pd-y%h<$JJyYrROv9*x)#a*`eUC3V`)>7$T*D|B&blwHvuUty+(y*t@#1kM=X2%r zkF&1QNP0A1mGyLs`mKY~$LH3-MX5nQ5a zrImA3X=I2T*92y-S|H)}2J`lwVCSA+oWM9r9t~7+kmwKVf-Pg^iD|BM^FnY^Y+fzB zbbySmS<6nW~(P8;M}CrM3_ zU=+=95oo~?Wtwoxc{Cz~$!k-`2JV~=;@$78F;lo{(%&H)6%vrc;knP;_~!WBinuML z2zxUiuN%Qu2&D(oq@iVGX|6~4)ff|{&4BF&U-n^ib(B?T0P&>cLk#K75WCJ5`TS9) z-V1P`$REz0v4JLpT^HRK4KlN?=-dNRjPl7N^;}HLI$paQ+Gn~=CX8IPEi`(-juDs^+0Tr zDf$&qGv7YTi&6O3tDK`MP~L)`cJH(^Q;?BKL6rN~d5KRLc?OPY=`z6?<2vL09TV)d z-_0Q6;{dZ;EA;W!(jMSdlKLWrHHIO<71`Z@FF1&;LBX-%Nk>T zag*Zi2Vdc?lyp-Nk%)@2+m=Rx7X%0>W)qe(O8I(}@&OtdBz|G#K6erlp)p+r{U{3Q z6vQ^={G#rz8RB=C6p%?oZ(f5xb{d0pBge>Y`Q;!*>O;pr!VtW95>@)<%d%RgEn6C@ z(2ZNmg}jCA`#O?Gp18^#0>LbS_w2%!L+d*zX{D7QT8P*gD#&CGyIGEN|9WTyZdJg1 zF%W^_&2i$4j?|7KvFL?lS3q<<{6ArfA}E9zL!^`cYU+$45A!HQj&ex`jDrvme183J zw$Pl;WaOFYfWjo-Spq(WBZqiVNTP&o%f_n3BF=0SB@MlK9oC@x1b*D4_7?8gk19_` zYWbL!o%>e3eT88fH0ir0Q;Vo=(}k&_XS8z(n{~_7X-b&fSTSGXcyrODemb`Dz58h9 zYDC}e;$;1;y2hf}Jeyk43>7QA)W#_8Lk)~nrF86tQQYZo4l&%AkSoSycp`JME?l&RgHdYF{*4ih=Z#XPMZWLQMKwex((&6A!kn-CwngNM zF%);2!CC8nQ5k;8mi7RSb_;Zex0aGj6ViG<{R-u63Ndg=q^C!2?PUygdPJu_so1C( z+5&{Tzw|>DqcNga`)13KW({^BGW#r{sP?q5K=|*t`9+Pyup?hkyt)5sNOQ2G^ z9=ZRqzF-l9B|&6^YzdSEz8s+P#JtQ5_y9daRPkO;!06O!X0tFISxHkG<1_tsqBE8B z@Db{8;rKs%eFr$!?Hl%^WR{gt5hY4Ol&r|sKxQF3DOpKmM|dPfrHqt9DOyITY$~LI z5+bXJjHr;z@4S1P|L;3K$9o(-Z-d{s@9RFV^E|KXf)(PuZQN+t$shKLPRljxX3X2G zNxy0|1ar!=gMR>qnpdi4N8bxSCZ6}&Y?_3}biv#kzxDK3TMQzfaheGG4?PC$X!{}F zbK9h2R7`HB60|tdeU+J7$kmTi)*-V5O{};)TS=1-a6MPbvansmQEuI`JA=SzrWPzX zw-B}mq~$W6jI+KnHXXI%K6sKPp#l=5VFPmu5RciO=h(C_!E(@bdXTC1YMxlGUv~G% zqY2sFIxSwV+`W-LK!Ch&hmZrbQ0IuB?Lzy>s!+y-q&<|hTpJQ(H5)4;;GrtFuq;5_ z0ZK{@i4o-i3xGKB{oFx4=IguQ6h@1{V&k1VKai?x$VwN5NPTJY-mMwt%{{fjwsDnw zhja*+LhYOFFPT{bmh=GW$FF6*f_anmlJ)2fatqOtI*qg@GgAqw(vQV;c_;(zBmg(J z6NZ5BvsApb8Sjv!=R}hNOmvEl4V)%#o;vurC`x%YUaS{|?pvbC0uV1OCLP`fkfxH> zzog&9RG&$SBir~!qJbjgAszfBphMEVCkY#AD#z0@M4AiBG|dua4Sdp_vMjV|Oj7m! z7>rD%ec4sNveH-cF!*p8xd+mr2pe6k71~WxN7jEFRqTsD$PJ8fKvoa;{Jrpy!}L|- zNSZjW{chJ<56{*=v@B8z_M ziXa8331Et(6)FJ;3z};4KP1Sw>@5DMsVZ4 zQGipM5MM%1kb5TL!3jOVdo)Wkd)Xg5CZE$`N;)ud`;Z|>cd_O-ET)_MzBYZ@f4rOY z#X^6gwWPr!@7n{@pjgmN@xGcOzqVuKh9xr(KA=a_SJJY8S*ei>LjdD@D{~eE&Pp?x zjLV~qvjcOrnCYw=CeiEYBIzOlHe+iv7!O%FYdj?U zjkvpHFaswSb+Cqh>&hp<@fSd~k#3WVk%2{b;T#ja*baC_THti!r9KBM)zHz5S4$`} z!V&||UD$j3+dI;{a>1AYS`KnA_A+RLSpEYpP2*C0&h&q8$262TxCvgqo2+v%;rlrN zcIzJ>Z(25kaYfwblc3Bv^ZNZPxigSKw-;tkBV5VIk@Z~O-=!BgVH7PkGI;=s(DyRd zBbl}Lf%$%nbs&wS-3ZzG6fnuEDTB3ECmNlI)|2)yByJ$$$t=1_zz*2!L*OSiFfj9@ z`#%tuNmB{fS2T-kT_%##Mnq182=C-}Dw=<~W@4F4_Moexv)cgS9c(8BX2&=Gc+o+z zzw6R(p4taf9K3rx3Xqz{QJAXPAcF`2dAmQK7^lnxs^1vbb_XAgkBn@&2xH{KjThF> z)=FKTXF1kq%>U#2fuwjd+FQ3mw$0maJNieIXOSK$ByA<(m=7Ic-he$MqY!>gI3aW; za2sjADjqr+fDCFuMIxd(W)?K{zFoif*%lWU7u!J6q=hs=hE%%|;NdW#x`^5VzE*SB zJ*$z+L8Pk-b&&8bNaXAZEr8h5MS2Ocad@GtDH*o#LvXq(@%C%lm8fd*dvD1Q266W( zUD@e@xlHI>_7ri5spwaa_RcAcIDn1}b;(jL51i07OvNH1GwHSZ0Ub%I5l%=;b7$}Y z;xpcPpm@;TgBnyrTEr7|bvtNOYOo~FH-B$%dC@{7K_h7h8QqXvIY^jx&>Kc_dFV*n zg$=XcZD^}y_`4qU(da!y*Z@6xUWhcKBmMi=)_EeU?J-n7hLBf6`bI!AQU!T`0eNys znO!>{j%YE(A+p$=(RGP>2!|ax_R7a_ByJ_<3Br#F`za!Mi0y4Vy9YRH3(`*n;(C?i1qKKuzM=-{wuW;x8FmSzSV& zPAI(xI2XSnaUe~NNUJJ>C^Vy_py|*}l4f3{%O71OX}wuKM%vqwsYkt||J4F4xmWQ! zt*j_#2UJJ>z*N#x(xQm890!Qwk%v(jwF|nZ%mlW)F|D-=u#{3g%RJUxe}9GYv|q^X zIntXO8eZoZ*V-TIDS=JLb`s_%T~=ZsC0}cfl6I1$D{n|?5vX&tVJUfn&3%a|`o!l} zjNbb{j!j}JS-4$pc?lY3GK~Tu?U($J65P3I=xgKrr3yJ^M@SzoOwACNxhlDp;Og=* zl9k}3y9)kK9+L%lN8DhaBDwf;vYjy_C)I5V9MO}&smj1TXa{$KZ}#i*P?F6@?*W5B zY;;VA&@5Eg+|}%xDs`T6@cI(jP6XqT zHG^LKfoB>6`_0=$odMnV65f|MILMYFj;HN%ouS05xFDDd8N(%z%u*z+CesAy-Y3GA zL*xjagzMc@ezs*+;5(KcvtYZ zyEgatMW2g{+Xn`~5{_BJZ*S9)_0)9Z7+N!cq(_0MBtT^)aGeurl}P&2WT%sEb!wiZ z8N6-5rt$wO7abP=w0qgReshu87CmCUOft&~E?Cr?P&Z2~8k0kd?2F(iiLGx*z09i8 zY*LGn{{ghd23JxQ^d1bGS>TW^)nDStl+j+)j>Fpf-#Vm}-q=kD2;y5oUgwq^RQdQL zIttlNex*5ZplEdG*~YlII9Z1A0_-O;+h}Emc`dPb5+t?jM=r$BJ_+$XTR*%5t?3e= z!tPIY0{CcMHu4Fk)4gO#I)hdsDGhTa239;PAH#WT*~AZ_l+3Q!+xqI|1ZagzSRGf( zvdA&rQF@dtTp~`9Uh#0V@}0#5;(dTVqy^YnA@cl=pl71={V|e}g71q*0QE^`D+F?o z1^}MDFxr?g8B_&|^E4Xl2ybP<;-afLGj9}$#<1cf=)L?$jk%8w^^#+ZG_Rq1FXY*o zPW+B6;QtU-S0kHqAVYXa`{T&42Zwuy-|(c7&VD~65b^-Lqo7?Yhe&=?!D@V;9ccBlc>e7@{V-30b{3m(oZxPDMyLIsPRKl; zmQfG0=-G9$eLg%qE57whGOJ6RFW_Ku3OJs&Mccq&8-3?F_Z#g3`+Dwv{dn<}{Ok1h zUwk)#?4SPb-*)+1&$LgVMB<@~HT_AKb?ZhwTD`huJl|v_?dce5InPiisC-01S68b3 ziQ1&i{|r?w(!wNI3<##Mva;ql)Xl*uERlWp?&tB(cF9pEPB4>Uj*n09yvNDR@d5NY z?<&1Zk6<>XP#_)z<794dX~3{zr1mtFtEs7-P*}&Id+0xKw~y@Kvv1#f$fn^ZPN;(c z#xzkBpZWFWj74(x8ZCN8CQi_;B{g{&@ zUH1j_mo2Ecat{QegTo!F7^wZjo;}m4hu~{mnT_Vx zy{~WbL`O#(m(Qd5?zKJo?Agy)G_0GW_{tCpBQV{v^wA?)*xokrEnRvhF)4Z3Am4W(TKsklJC=B@w!=PQHB4IeyJMf4^>wJ;W ziI2+f*}Inxi4nS!Ea*o!n zbeOfwlQ?95aM~2(>A;NgRD8lit9tf~GxkD!ybWHNT9wE$*4DRKcE==@Fhg3*GN1GH zO_R-#DA@!APJz|4!yF#d2kyd-4Y$(stkH?D48#GYDx)AMh%z$p#>g&!a{9@h`-gg1 z-o1N=am-7vUcbIVMus~`ky2k(#em*?`izTrjsc=Rdi;2~loUHgPf;}~yHfx@! zrb%dSe(mMG@8G=*hQ}kU5gHDKQ`(>CujL%oUT-uwR}J%!|N0sUt9tQ5D@Jrj#C&D# zty{}57KIkGY;lImD&(#?`=mC*T3KWBX6CQYo;@1|2U~`b(+v#`-rv6nW2h=SKYtW# z%7P*yThcI3$%2gir3??eSuZ3kT!ta@XdxYpb>A@9p6|(wccM;SW^ZrW!FC+`Q5Z~G z=g-5#gXtdZ*eFJ3J#aq{kBo3W?}q`JjAMp+&AP#{z7z(YgD?kNR1W0F{NNP*>hW>! z#EFa(y}b_P_b{4t92w8Dm~F2cm>C%j7&)KS)NFQi6m@rZx7o8N6a%dLZ(B!X-9}VQ zZ+O)sPg_;Cro$z?%8IC^CxAws_#R?2 zE?T^}44Z85i^-@rXKGRs1rv1dV?v+%p+mO&_cLPMM@}-Fdp?Q>!Vk4YPr3$s3^9WY z?s*-ir-jM4U%H<uc#sU;8^c67Ft-{LTFKzp zPW;Pq&*le*_KBJ2=?LdEwzct-G3;O&Y;A3iqSLzIrcL~4mlo$XBRW1gc?AnjVKi6l z1&yGfZ-GmG-rNgCCamjX6rEH7Fx>HTfMhl zDMVyy>e`;dOO}+9Qgs{G#ro1)#nn-kyS_>GE3nx9bFe*^m)w+@LJbQjVt;c~z zIoX9qc~1j(G^eJoX7PWcb=_|BUn`S*Y)-v?-45DHikpn3xM`+}FlUC?79kF2OsU4h zcX)Me0lLFSYDGrOyn6`HA_X>%!$t41Z5){f%PQ;oR>j#_3^8Zkd7Ur_q&Gr87HzNo z!iQNp?5=Hv$K&G{J*ud9hd1nonaY^+cIV*I^Ez}0RTf3QTxe;F-&Tj^X@QbXMojko zBN*aL#j?S`*LmQd*vV_$+ju_v?OO{mtHMRV;i6;tckbM2%5&kxvz2sxE4M``diTA9 zT~+LIa&qUsAK_qU-~Z0XT?R3Lp5i>cM_!@+eY^0__uyL~IS~Cb9E68g>;BpNW?W__ zaha8|;^u0iG4HOIYAhf4jEk;%@+W+;*gNJIHTy-tnq0N2?^=!?oR!p*$2&iF4Pc?=SH-ab4q)}BluTY&J}yA z(EBo%UDDo9NbUW48p_Ye@y`KT*Rrp>I);InnM^#i>erV`F)`S_odw`~6{dn%UZ23T z!!jVF%=NQ&YpQ!$Ul|^DLo!Uy$jGSh8vZnTZV8KU6qzi_@6Sk-9p&@AP_)0X2698z zo+lhzQVnl=Vx+aCTZal6#E!U4Pf_$9rdS7za;d4Qc{E?b{eV%ay=^Y5KBR#Ck0-jX zO8DoD;-cI|hqwM-O5GK!mN9OkIZg>W$CbFD;gvA@H=to5{c(-phjH)f8p;_kWxf)y zkcI+|AP8_*-%HO_Fjj{zs7Fr-o1hE>cTUI0hcokZ(Oks%Kr$0sD&Vsy-;9ee#ilMr z(;Eo-H0F5Y5L~XP$cK6B_kBl2UtCf;fv``ZIGXS3JbS8j*sE%>kdRO*HgV-C0iMwf zB4+&F=}xs*^C;Sy-q-G^#_fOeC0lbt>s;x}N>3hjEI31<#9g~4H*C+Vl*st|cF2bj z1x7r3XuqK!Asp;R&KCBGyN-7iW>SMlq@S6YJOICmIs^6Yy?dLX(R99hHwS@M+M%|z z*7CL4kt3UeLqZOAl+rwU^oXG>AT~DEp{-D!kB{%i)~>5#-*LlO%EQar@4!3x5gET% zcP@j?=2P%sIC}JGcx0reM*UeLJ3cxT`Ze*#Vo-Oa!*?_}x7oMvSknI%xTi6i;*c}fJ!s$V+Y5>!x7Ff2Tf zHvjvPT$xwmy)WOIP8M(8=E0nAeDeD4f_z#_(Y&FBr-f)OeGAO`JF6D{zWg5_3=S3^ zr*TbPkay*s&%&DIdv7irp6*?dbGAqB&)=)NIrw~>aqZ90h~vz~yTFXG^LSwU5y59B zp-O#AJQfSnh*r5%R2cH70J1XOI@O+ClwU}3>&(#C*cvp>e2hYYN_aFiRTx@3HA$PE zp5DJ;hIQ&|p{Qe*5?7w>o~twWg7>Uj4CZbgrB}IpA%%^HXBDj_%OXh%%A$(WF6R%N zy+aeL`s7QNquovqrS4&!q~60ZArh9nG|BhYQzZ?tKT}i;e_!SQ^MlLDZSAzb@66yc zZlrA0GIp#T9J;~xE^rBY&ZipFuFsd56RQ}_@RbLV5qu;Ar8eDY6-DJ;BCTn);lc0a zW|Yzt%@g{!cWtmJ_Fw$u$rFdB%;h&)6jn?aXvXu-9UJOm7*z84LW|uj+QM3Nn3-bJ z`j-D@pheKohwYU>_awW)8T}APcfFqF*j9F%?>@|flb0?Dw|i-Zrz%vd#zjnMzK)#m zTEsQFbBfo4kzzGVclP)0{Cy99fB1MKC3&OD?+txm)li^1XTZ#Yoz7R(MXn0Q*X6R? zT%6@IOy{K3UfIlwk<~lXOp1Ix3)@?lQijE5vL$CR>Rxi)IyF#zQXakf(oX%sy_>nH zgHQLYOU73AdOfyWpRL{a+4eub_OP|N?sVHP z%4#^mxaH-Njx>h7#-U0ivp#(IFgX`6#>N0iT(YbFy4nL5c__<-h3C1tx>6_zHOn7m zyVcsIIx|A@qFcCdAw>oG%#I1RbMsB_IV@Peem$iW>gH-%SJ7`@Jq&41UP?`hF>pLH z$r+QZblT8BkKH3(+xwd42A>CMHqXi@Gt%z8YaceC;54`p&2RP3Lh}vr7-*`iwfvnq z{P(9RjXKoe;NUsxUctY^N!xa;TPbgKAxde>^Ch^wCHz_qKbw!=LdjTid-Hvwv>f-fQ;$bpPUxev+6XX-jpm+P!->qxp*e zZq;|A^M7DBMO}$r$DNPLGRil<^;cN_7w)bT=?;1fB=EYyfX5>}%6(btgDh_|clUKb zBcVtrBKrf;v>M|U1VItx2oOF52fxR_&WndUgKV_fUP$mx1+oAYGrSO!a6 zSvRw0P5Gt`_>PQ}(W0)hiy$5jc085&_m#}~4NHtII$7^L{MzMg>l^)EM8~n;w%7I7 zuEGOs4DlFH_{VSt!@IZ z(*dIg#dij|SCtyJjD(M^S}qq9R3Ztydp8r1P5sO=X?FIQNZrQ6Hb#%O*A+V1 zZRgE5|Mi%={|#fbRqEU{r6fw(Z3d+lvq%>eazme=+@nJac8 zrRng&V2Rg*vk^fWlpt**Bf39mhP==-yN(<=QXV)zU&!}sS9dp|DmFuB`3zk!8P;h) zcqxy2z2aH+Pq|Ia&Fyg7;*!HYvDERB;X$BaL@*Ei)cyN%>R!iw@8a+_!TAM3zaB%1 z2@a7MHtz?5%M7mTZ&A4ih9F#QZ*8hzykQ(g*D^$crge_ktGf{_cZ}QeoK}1<&Xrk% zn!DaXozMz2n=G7J8u;sb{rEMd{91 zSG?@69Msnyry>mn2?wyv)!ZY-VN(2>=Tw}UuH88rA2CK5Y%vKn80h5kWJ_2nDZrs=c9QXSx90ApL8N+=+RuT|3a3%VNYF1gphgYl8r+e^Us`JrBUGIL*M zcfA5ky3rDNIYHnyhnE5K`L3 zzFW3L0+rL!(d{1XdmXPFxCwh;_)E8Tj4#J=TJ2b|GDw6-eE0UcI(cHqXq|_u>e1u^ zjjLX)0-H44-LuQ`lyTgGfU}6-zGX`(7_kLPVt=k+guA1r|5r?6uUn(Zo-e{$pkvHi zp2l#nUbxq=k+N0g8;;vHnXyZ`a2O>5ox`6%BKw<`2L3i><+H|6UziP@NH4@h}Uo*sr8kAwWCeuP1E&ZkeG zy7K~FKF1j9%)r?r1I@YG_GY_wEkS*zL;?>dXQ;S&9)*HYq!ZNwuO2cAK^WX8Q2qL#nHhXpG>B6?37#g5X`7>HpoGuvR$HmozS3w}*iwMhL5vQXpul zBU9e-=g%+p{p-?u7gYScZo3IARXqs?C8bnbGyICvm6W+cT%*q-J(&+0(;7KCnLN0Z zhsc^Kwp}?Sn&yOK5H+jN0;-^XcA@SG$G(=Oh0DLRXXR-{CS3bcyzVao;J=5o?)>@l z!=FEwK@tjq!!#Ks*N*+ucq&Z~4y~dj?S33F-W_K>(o9eNg61g!6Veaz)L`umyj zjnrBHg8D^eqD%7U{#j6ccvN#KSQ6Ocv>hdEl?{6}=?&+obd}IVH><6tIVhQ$m!xeO zEOTO^M><1ly|Ch4Hz2u98lnL`)-JW(zCN|72(TynoP`Je`Y~jQ*0p?qNg*gBV{IP% z?ty`Uk)!1gA5y>I1&=fzL$=M_4b$gPoapq_ROlBZcec*X77-2A88yh<`4Nki_%``B z)wb3ZAkvcMuzmY>&~wMP)utkSz%!>JOH2U_8iy6%xEUc@%}g{V*35d;Qw*f>$^;1! z5s|x_7RKD$79+}Nzi;0@kcBa6Ad|v2I5i{%&dxX@3j5q2gnv82`8vkdEmHE$kt_mJ zzphA+js1-2F<+LjPjh~5H-iUj-mH56=gHYWYkAHTBsj2+9Z(>=ez^+A>0_$b_R(Cv zS6iI;j@GD;BRoBApX;rn%Mtq907r0ukLFMVM8_ zdw?d*%+3zJa^*^L!4A>2*2RyY3%Eg{7-+et=du*3FKPxC01fYY_~qO~!Lw)28tCf> z;Z>~Iuz?E^h?atS(z)E++^Gt2c#5cVSdnnY?C!O;%J?e%ckka*-I0fHtJeZo0iwr) zDaP`i>(W>;Gs8WJ2oVn(8+n2h*}kD%YaB+(1xm&m8V9a!bx~rwwB-0OePj2ls4IU1 zK$E)KrGK#4OAU5(Aj6)`OuMF}vQAW;e=JR0#n8Z{5#g-rs>G=DJY7*EoHJ+>mnOxq zZ6@Qb*2BANLu57PP!bwsHFw$@-R?~M^M1%DN~U}FHgbeRRRA%z-oq<=q~i7K9U@3$ zB1d6m3v~y3G1`0#tT*LLlD3O#*o6DU$o^zSn^H=Ad^~xsH{ImkLu5XE>5^-&YmBMC zzamQ9PTD>UOJ9)hap+LU<;wy@O7ZgYy4|xGd>h%GI>(M3y8!*}T@K&*np+YHZma&^YsiOivwySDEmH_6cy8jM19^Xk4N`gNDC=Vfu_yoYg17a_jbA{wwg}V|jUbCI*ruPXR^o=4)&CUg7&< zYTRV9ckea*JS@gyBrQ53=DT*qwj8-EcTi1)xRtu|EM4FUiI~6C5`=s4%!E_t{jI5n z$f4iVwn}7y{b97d&IA%<9L7oIDYyW}QQy-!FmRHIOEk@L7Pg2-Fce7QEV!SapMrqb zROoZ}MN8oKFZRbL@0_wrc17cY55BzChQeo`oQCS(>srW#pNOHaG`7ue-aKe>38hkP zA^nx-Kw4zslM6ysc@oH=UQ{nO+_7AZWa_J96uTb+{4q|sc1cM@dmizvgp)TE^ zt*6IRy?|GyzGY09S7(p78OsmfCF4xz1J*vIGV{27{h$lQXfDOs*%?F_ zUDKO4$NNlf?py#nY3G zBInu12_1EG6Wy=9fM5Rs=$F9q{J=tpIgWGJt`N?rnuzux=gHuWSw35G?etb^(jFhV zR4~EaNl#PqZZo_$b~a)6O;3NG;|vCX^0bUskcOh$e<3B%>3>rUc6J)?sqrlRJ4TzQ zm9OFKIia>%TuiKb&mVZ%WF_u5CQn0BE(zwB$&ULmRwPZiCGmz^~n{3OIS<#H;4w&%B6tda7|7f9LNX;-rtxue@iHqh0fv%ur zi5g4l2Op{4-MV$_>20YFZ|_UtBClnWp+X36r7sdU;xJ9liv$Teh5ZaAXU$!yHQK`R ztP)m*;V7GCpgC8KJ_F$ddcq;%+y5RgKh%9|#doN53=0-;T?+^Z_zX+djr#R6GFr%M z*f}`nsC@IA0If{Td@UYO&J~@5%PBs+=W~>Oiv|+%{QITd+|974>adZ z+Vg!jcb=(e@M`4)$wik0al#DtFY#cU;(43io8h!#ba}!@voA8wBXemj@B9}BZEJrC z8$a)vaqnGNSyTe z9U;fpH-yh2ARd@6u*u^3=(d6qP3m*54_#fxxANfEiUMA~g6QV(`bLaodwaV8l*T#B z1ndA7iERI@B2R8$V4!1Y=oBW~o|v-(G7>u<-$@jW*?(Kub+crWOT<7XbM? zyvY`ZL+TWQ@G@Ch6XSbe%>DT1$PB;h{v)fp?urxgKlp68%uH;eYV2ku|igs2UMhH!a(LslP%!8&;E4TtnxT_p|=wTbIMnZ!A8%d%jnh~6c|#S4~5*P7lVyjoN0 zwGw-(=f{`xuv<@W5!#}w`*^g!(Ua-GFZv2Orzp!`B1xT78Q>s@@Zj2U9UK5mws8it zGi(87w3dkc=0U0MBB^;tMCrZggrBxFaC$60H`TsQcQEWH7245a>yq>7{{qlL^73XP zjpM8J<75vo8ymyuJJEVF`6VRE35oV zxosGdIf~7mtFxO+6+C)_ubBn>TDD5%xjREpm?eePT+ zX#Zp^Y)TL?|MG!sITCk}5<15Pcs6G4x++*Kxn`t|5FY)*H2hFW==OCbEJ$y^B2C{$hLD+l* zhL&yN!Vp+?Y%qV_amY_ti530`NymDT#Hep*wBZIoBep9i&G=gznnN(yjl-wTz44jc z#*O9-8MH|oPLIPJyctme_%sR^-j}mr#flZ=(y{6(LCic7f9u!bBX@RxyzmEeiwan| z&MH0XSa>>gL~NP3xu1yeGS+KQudGbibv`z438=d2?g!FQRBd&xO92wU6bs`EDs_V_ zZETDbhh6QQ)8-fY-b?-=6Mw|QA}Qp%q1ovMDYmo%U7>x!XL^&q&%S-LRa?Y&0<&`9GY`hC&;)&Kw{sGn%t&) z2LzBfk7l<6B_Gv~-Im$}Jlx=kmKE5arh;mZkVbip?- zAo@2bbIkz#&=AqSw}>%l5ochgjPz&K_KW)GFT>u=wPt4$9MaG<7tEPEmmMqyF0`z* zbtzt0{nMtKcFxWxv04QoDnpP`0vy+TdNw+m7;%zeu}m4>fjkS=5k}QG$}o2UJR}5tl*3g-XV;)~~Qr}efKZPbe5JN``P`>7GDd=Ex=-74Yc z`HcIs0}!JG@vOD7fz(~@Qv^~7=fO_Z~^?bX}+y@ZxMS2f#S zq2nh&vdcU1r z2#H%bIZ{+~x=XX*OEU0-U$pN2ZCDhSDJblUkKNK`Euzn2b9P%Q11qZuV-@+p!cu{P z1WEUBFpQ2W5jsx7&d?{G@QEBWO)@WOUoiqdMTetB2do+%VF zq$pG$O=VhRa))zp2!FuN#ufy&#TKKIL6}`nJL2qo%PQ(Iayl`lojLdJRi0U~IWu7L zFrV@fzIOkwkrfBqN&-u^`GL2%@76g7$=L}|Ht)a%{>TmyYbmFX>7e+Y9?^F$rEb&* zW%npH(z3JW>O74U&xmpU0w$(%WHAQ;3KGZ61-@;7(F8!-;=UUVHonS{BgG=pi^kW+ z9|GBb|HyQ{133kZ^L8YBRgtfHjmQGlDh#xfsBu~lF!1|_tjdqD=85~0WcIk_pa%~g zY=58n0=t1)oDd(+DIb==$;rt8lBk> zZK>a7E#GTs-eh-GgWtoHTCn}3xcvZ&tGQ@`iOuvHs3bfjM7n@==xJXrafhMPK!>hBNtm_0hf6 zh-Gy*P1d62tqP|v4}65E>4@{4jXZfW)1gk#b{C9J=oYVrr&ZxOl2rme+hFmxt5=0! z;h2jxA{RL0pEb5Z>T}6Z6jb3YA~3ud)Wf-D4Re@o{7#mFbK6qU4T}~sp+>+ZDLcAY z3Cjo|S7a9a=p;geb^~+a2!M^YyuH1ozA`!O{d+_sfWs9~3h=kM#nG!n8hlIoVN2Z} zV}nm^MQajvvbY=XOg9uB>2G9x8Y}(~=Y@XOyTDVcL!c#q(hr5t9;x6lpg|BDiC2I!uDuA=`DOPW-Y^9mkYY{T60m zobal`PM>Cru?NU8kf-My6m37T&5z$A-(#!D+Fdye6%~CYTV5xt)BAsG^=c{(*hr4e zXv1PzbOc!h+#_+Ax4LJWmX_A>$&^UJTJE5VgUba|UcT{gp>K8_VyroPH|t|F%O5o> z?XA>bq_@>Zpo9RaX6TM~MI&OZA&`JTnrqTq&iSIELver$qX@Q>G4l%_Van;vxR!Ti zT~|PTD)gT(^bAttt}#O3CoCSN9Ac9m2})43mPxF3Gp}DjLvn&p1tAbx;8W zd&+Prni*)=rNv*kz{?`A=_C><=Qr8&2oHjMvJM8!WbiWKBJo*Qu@jG>qBWv95wb!> zsdm7?0a)P(CgziO*Wl!1ph!tcE!yD7Ni5`Fdup8KyBM?V)84+FySlnFs2YV`Q2?FE zScNGawE%T?6Ax>nWXR~m~klRQZq(uk=3lf?+fO;`i#9eKr53ri1WWIoe zmI9V0MWLdi0;h=sTSulROfzkcN(OY6&!edL`sQ!5 z#6l$IF_;3eBSR&X#l>|+%uP*8LE>WZb0rQ@*bt|Q=LNv?k@D=+l|1E%;tUtO0l&u=7 z#@V^V#}JC_?7sAd$4A$;oa;{h1B8G4XkYQw$Ldz!&CVt#MHZ|}&TWN@(aw`A_Dxe$ zc;C*OJ9R~1taF;b4@l*Kh@76hw4VHy7cdlqJ=4Mgs@V?+uoN<8qJx!%#YL+MTVS=! zd-r&*0q#U1@Tjdkn_mCAaz+&KqXR!=_}jNi_!u>wA}f*t<|h#hiIstwiG1l0v_Ca7 zOSulPCukkWN>Krw@EJw5j88dfPLTxo1LS^NqLXgeF>PCi_#h-a{cULsfMu-X!29>_ z$>Aila%G=!JHRXP=3k3>IDM`Ng`_DT%RA$6SgHdPe@2eSJoyzaZ;ru`e7j`6M}57$ zB1;ZYVMJATnPtUF4HdtJatd5;`MU0}#!Jo3&13qnUOiVM?C0kP8qnB92~L%!=H_w$ z0XuUX1WV?w;DTd1)E&E!M#baKYO-+~8omtaY(TVJG>cGryl+REJ`KFlk5jedG7rAj zLP~S<|o{ag%Ece7j+bdXOWR`gwRl zmhId>Q7uh+?~9A86D0T&G)EL}SQROtL5HXg$nSR`lL>es^c?yCuwZXUp#)Z>KHV@i zA}tbeiJ==J1TpH`Zv;cW5#_PV6Rz{I!EHZu`~%u;TDt_|>=Q~j^7$Rrs-g_54%HXO zaPCN=TUJ24f~P3-jFI9JYVLt{ei9^M(A6wUp}0*?c*5YtjZMowhmGc7>25c-Otxp= zJIdzPO%U92{rYtWD}*fS7Z?X`8{~kVuN9fOAxIbmpV~&|0$YQ0rm1|0~2+HB+0l5ALwzPL>O?GhR|;V;??mK zBSmP{s<{-`_G0mb5_s{AZ;2f7f6>seQ^XqfeM-ltPZE9R=!-K!v>R=@2Dyfg()Hm3)7Ptp>D$XLBw+c)Z`RUca_Ro?p}ew^9+mUl zv0lBkG$7j9sLLiMC+AWKoP5?_pT3a5d>{f_G&6>^`Z4_6glZ=R2*8J0KzG{yicChK z2ShQrX+El7gm(oZErsXKBz;B9Ho|Zc`*rz)+FU0Sf7Ex2CSPM=VG%96O{q=MWKQT!;t7R zzn@DfE-rRxytA};ddwVq>OGvOgybZ0@kp-$j2z5_4|(v3*>AXm23H|;(JwDAPqatr z&jLTQ$ru8=h@{7=KOt~iHL>+?qdCPQe%r=C*N>P@U*?`sbTc$pvMgU8(p9tFVL{FO zZDF;WyR)ykteLoe+w!sLuM_$6|C*AE+6d8rjYgq>SB%0*aibntzbXzrnyR@2Ec}Wh zyN-P6+KOfaTl@2%%ZfQOQ=mr&BfX@dgw09JnLtKvX*QzPf&@ZOjWaoLXrs$whN+>tJugAPPWTAb9|hq&y1*cq-e=F;ce& zl^g;`mcADSfO-W~cx9rs48ir~Q$NK3h0)aEEXXu! zIK4GJEi5geG%u)IguCnnX#kz55-SM}p9B~N5i@KM+M9CrFQoW1y+>lbZA07R4f|g3 z5l4V@O+^JA3V_OqC{4<+LIXshG4ntP;(Fc>w>}^ww9{jFQiR3Hd5gV>8nLjfEU7yd z4}yWXnV!x}ebvjQxu>eCit`uNQxJ9Ks8aCS?BO9z#75Lr=QzIJX5R`G0j)#UGB7dC zA<}vW3LV4J)Ay%m9O|CD_Rf@^j_zGkmK|XbBm*HH_O=$>lbjyzRA26vg{mvU{G*cP zx?v(c`n-;#0SWaZ&QN$CPKxa0easF44I#nF!WDajDSyX3|2Nt8DQs;pXW3wLfO2Vk zAONmgEh!@#Ng%0)kOSqCCIG0biR1IBvqgA#+s+Gj21~DnC#Lu`R$P?vxc6&aSMPZI zxA}9A92_5N--lZCYPF8(SHGjPOyKS3_BelOUXIk7d@JLMz|Pno%6W6txv5507uC2x zEqkWq%kW&;OVyJ&{@2$5PP_NB#nPYxGxDO~aMM3B(tD=*3rT%AtI}Q|(a1u+Vc>0n zQ(J!q8AB*Vi;9#Rd>kT}#90tngeQf7P0`lYj+wz}-tQ8)eqEP!JR~>*3lLL3ql`*W zL177QTYcL$CVoZlWlR~gLAZHF`|hMCD7?7>$@PbJP^I3csHj*!b{hs8XlnG7Wy_b- zu<|PgrKhikmreT8Tu|5N5~B^`uEbwE*ju|i5`3qP%XGWf=AE}pkCOZv z@^DJhQ=Ilpwvni2(yFE4Ks!PH2Qj|WREUAmN9Vfov)d=&jFS_`Jo zV6prQq-mmJmX8qT9BP)~DW8K=c=4eB!yT;D)aF3255-1L#&RX03^{WU5X=fOF&ze0 zXK0V`M6TE+Df%9@D<=li~nyJAQ!yp2{iQFRx!J~5dtX(bIYnbIni&bO9j7^4_|Z=eszA z(qDWsP0D3Sjz_J;tP^v>$!bc9Q1e6NN~TkFO;((aBKk0p_+mqo20n}LenRvZeZM^bpmlCBOc(Y-XWVW zFHGF%m4e{y`=-6LROO_Aa&go2E|fEa&Ss_%IEW&y`9HHLwL~0z56j9_g++{rXB>nZ z(oA~PUOq%gFlG4NV`ic@gZbWsqyz=2q)rovy90VG_*0r?tRIoUU23bC1g~)4`?DYn z`ffchNNMqj;9ebFU0YYzC_vH_01qO>HprkUyTr*Dg{Y_~6i%~(z@eku_Zs5g1kK#m zXV0FiBE3HneK;4u5hcqs)jWO5j5gV9dAH%d4uUTA4wY@DxsF_$X2$K%VAql}`eT3p z2_O!xH1YwD&b;WUPZgEs&g?Oi~^q0U{1F^u!hwDG?nK%xd}L$C|>ucfrny z_H<(FN6lmA4lloaJbX)_jAhGFUG|NdU++Z9yZmjy|66V4Rh1uJXRo<0ax|DwQTg*M z`<6&A7OvyJl+Ndh0)PXvU%53KU)xW9G%GbZ7R}}@%>L8-->o$A*A^ll59Hv7+W=Bz z=HzIYnwp+>)dtTAl(hv&1L7Z$H5IER2`<9!{2F4aL%huL9d)FXLeeJZZ13%9ELB?^ zIrq`tZ2M^I+qaoMU-uF!D?TnZmJW&>9IIS@lX!%pwkI^v?MfP9g(i(Iu=oZH9Z^`n zCq95=umL5$Xp{eawlC?!1YX5b6|*9%^P7PJ*=UINJ!ZP>3`VS|c6*CjW3~JDuPZh~JEASW#Jdb70@r zsy*vM!@`I#V{*r$&RYO3LNy#X+uU4X^@Hwk=Iq(IxQqG>YwNYsh9dr(dU|@^WBXtY z$82AoNFnqvlFXe40_JobO#~EPXn8Lm74e$H0wEre=D#|A zWEgYOjUz5zEh$P^d%ir*k(U zc91GQlAW;JdZlSu{BKbT-eDoD;sBIO^gC@2cZUy08Ce|pVezIrX+u{IT09sP`2^|e5 z4;4h)!|LiABfPbC%6MES0Row(jV0HwSI2=x^n0SLLKLlS6@Pito4?Q%Rd$Gn`rrzU z?^wWh2byHfUuy9`Pw+FU;`CGmDX)p4`l_It;X*k5L6lIMmabvFC3fR^HdDTFPHxj$ zj?b&lyX|+3m}{LU^(gay91TUEiQt#8!6^lYYuKl1UQ+gnrW7hzF;9g|?_Px={5XII zO{w`}?+H=Bwn_+Or%#_wGMYwHIh)RMs>92x$CC?Ywqf<)VZlFeBe}p(P>~gcvZs;U zKsIWPP{aKoxj^&hHaEWWcOHI#v%B;u3XNz;{V2mVSmxJ}1`aMRbi%Vd7U>oZIWC9n zz;Jsd>Zx^beJ=h*82w6n&dmG-$UyAAzPsT`3H$WYd^MsSh!Q!s4hp1=&{B0kG3f(s ze8~C6fn_>-d&AL5V^MyAv6eSK8+%=U1*(ifmG84qh;7p&t;eQU;@3}zS@TbzyI$Nl zj}mn6!UmL>W^UvjFQkr*9yTo>?@U2!26(jp>H>8LyP#kEEsW#lNHj zb+F)nu;xfCX{-U5KM!=rvxE)ISPQ79U^jRG-9`~K@;r)Zp7UZbR*txuAQY)Vz9MG! zuun{j*=pvZ-g7Cg`Nip98k4Wn=Y{@1Nn8Bk`@gK@SB)UBIvc{eYdL2%%^;8=+v@Kb zx3mikI2csp5Q-Oj{A+r)7e(v=YsFCwcrHz9Qbx9~Ji8p7J;05pe8+O(szw4D_G#tx zO+eN8azdBr;b`4u^V^11^n83ZdU^c(DmsYgrExT_StNM1=Yj-hmzAs)VmGYdxGYLL znL-qdqfj#PE?*L6$)^p%VqM*s^_RCtP%!159?pU@uOK8BU~htoBKj_{Cj&ePRC_4p z+|_6ev%$mUAJAmQ`?shk8;v3${V^0On(E2NXNyE6J!#mR5V9twKUZop!$o(LYx#vB z*A~`obeEK$X!vhiN$Du%{2%)#ZYU-eCVChtD6p;YAqm_J0~7JGLgu6dAyl>ne6v*d ziuskUkY9UqZTIxD{4r+4a2=3!`p=m9xqbhdI(JQC?BA-wFP*Oaqwb+7=T?fkG-&h| zx-QRWktf=Ybd{)HaJ{gChJx4C`SMwPt-8+toSY-gpn(?ZB6Zu1Ud_byX&61Vc_+XW ztkVDNoT8oomL1qXhFhx5wSlsX)=E@_-pG*|!8o=Nc$i~+?df%c?QG9w_V)F!R)goR zd(`tyvE~MwEbFV1HT~_=zgOHZz2N0}>HQo47RwU6z;&YbhUpa=K47W*S`6gZIw$Ji z0Zj{5XpU;w0@Tddb<)Kh;5VNMY}C?hG@>=!wdiSx*aoVDsG1CoPk;+eLd2l5U-a?X z@n?bGo)A)Qfl|)@JPC~hp-VP6me$m;f;GQVp#Z11w1;eZJmPC@w!JnSM;#zyP(t?C zCDoZ9`}>M2ewnkGj>hHF>1u5alG)qbPxFOH9M56CP~5ZdG`&k-_B61&-w)S2l(mRC zivKJ>y!o^dlt@bMe#CGQECjw%?1@VSpzJ`egrQXHgG2Nv`vqQ?!=M%vg#C3RBMr7q ze@u-uv zLaj!&S%yJfzz&w?`NUd1 zuJa>;q7Lm((Jk2|X^Qe2H}d8)Ym8(-9=QTEx=7w#2tK0ut%uizo-qyd}pbf`!g_NPiP&mXXN%-@<8|*59GvUNAcS{ECn# z<`B^Xh_ki}=GO@K5|2y#`8t1df_7hFBrUe5`pA&g(^-z86(Un;cj z9C$o+;8f@ea=s&J$pv4v80XK|Kz|*Wi8A-US_w6GL`8mJ9a`xAy2eg7ZOvi4;i3me zX1?!3UBrrI%g}oQH-PYM|JTtU-bTuhQ9R}O16~uQcXr-xN9KwjjSUPRv^IDKmG!Tn zez%$(yFi}g@2|8%JW@NbndLd|=fkVfhx?rV_N`mN2(^j8m8d2!C6&bqlR*`}ewC`G z$i4-$N1;HWkn%7A53_4N|FxIffWASjLl8d-NVOO>4F_mvnEw#$MLV!QVZ%ehi^t0N zG-X}aY$G`qm$N?7L8;%O^-9|Oml&%;B%Vi}a5a&4SdZE_)H2Qv-X#@@OMH8OUo`p9 zD=u^BgAPc5hXCn#CcaJpSt!V2e1!vu?qekc&=4%>w`gGlBP>9=9AL~f!y)KqO(lWK z;7mwofRM-wcg|JHtX+%#5(aBvb4QQEHK4cWfoNCv>WTRV&t|eSFhcLA)6pPm$;!g9 z@)&*={hK?*PcGTO1mar8qcM%G&FjbYd0Zb?M2fX2e+}*T&*(%Hom`-*(XAuz-SqtV z^V-(r##&^`$Z6q&4hBz_3^9llNjjbSJGL}@{^fc8IJlRZHs8lP6*X~u6|r~E9vevFq>Kg_miPtG6DHKf>W1@;4ZZ7>n8OX3GQ~s*9JR7GSE7ijUcl*8zHY6olR7KUU&jXdSTqMz zfpCIE|1nSdb&GMiq{a(;2alPFXv~dJOHq;FvFdfp1|h1XfgMY4FyYsO;R*bmiA0jo z=Oh_QC<>H*ER~mQ2EqNSJ-|Z^`xMpIjl2#m5XvAobi#Q9RJa`279D1w4(uq1f&&WH z1zNz{+^2`!-G?E%?M8~Z1@;-Uwk*5NfJt08FXMnAc3SYmB<+hFg}ZZs($$QUNRpSs zX-Ql-=y>N?TRdEh^C#m3=}!ahyZ2=(Gv@5_J@4+Vxt>h^SjUM3zC6 z&f!ZtMPqhD)*+h~Hi^iY=}BH+)9^NiZZ3X{B^gdzI$Juro}4A6yFgr^-z6a~0qUpx zAHLoLuIIh~|Nmsmh?K32auSlgqNI$hN=7NEjL0avsEo2_N;tF}5haBX4H=O`C1h8K z2$f_s|M#bJuJ5_d^}F5vZr|H=xxS=6@9}y*pO5wAE#CCGt)e6c{YY@T_@OsalIM9$ zN%&cm@lOSQV9rvo!y+5}i!3Av-wFyrR<;T=4NwjlYa z=3C}py#y|~fBpKZEN|0s%K@qIC|mvsa9M6>29qiQd%&?Zuv{gzqOWrqwSU7hc>|+K5Fcns|PW` zXPtes!meFat8SenQZy-1f9k&n2&N z8~~~i_dCQ#dKIsKsUefy1gzo(p6${c0;6BOQ_jC_o?Q%>VVXs7a5ssBLpP*Irh(jn zwf@hbVb3y<4L!ANrSTja+Q?Pol5_K>Jp(j;cyAJA3!V-It9J;(olT#^lW!H2Z7s-Z z`QxykpXHmR_ss&1Wxq4~r=Pmt*Yv<202^zon;f)ir7zt>_=A=}1JV5k?yo<+rS`B! z4b1jmh-eT`+>EiL*5oD!M&C31@;@i?-n==pXKx9oCYL?>2BqM`Y#3n&`_CMXA!`xn)+XM`8_>wwS-gU&1niyI5xakhoM=i>yPFr4$A>kzj2 zRyOP~f5EwZAZMqz<2HSc8_{j^*MzMOffUAT=)j7&h7itlhQ845aStDQFe2QsP!g8tn*&@@XYemSVR5`uRmW zc;NwGixcjDc<{4m+yaR?dU@s2i1Moy3^ebb0bllB^3njim;)cdqscCd>feujDog+U z6+gc5rQ$V52ad@59snguYm)Gmj7rn2wCp{JsE`$_`$pe>)#pL~wH4nMgBi|Bv8N|X%r;Pmf zk@!zPW` z>`~;0_$);*ddbIb|94NyPkkJV-AWn0Jp%s1tNafFZ7zIzt|8Ko>xe-5C3+tixI_c) zq#2e8@^nSZ;PJ$Z^!I{cTj>2mH~(R)Q@^a+<~d2-ezh-Cz3v`5a->P_-L*$1&3Q-1 z5jH=6nVG%XySIgEJElzC7QboSNHC!8-5a5+Je?K`B9)hLiWY=q^QJ~#(E?|QWvLQD zkK)bxKzXG5&f68bdD}~|xm}~Q$GBwc1 z{_L(~@I{YGZ{YwR!GRj*l`^H473QJ{Ww z^_Xc_pFXwe^$BClsXqZ#u2D2I&p3>XDkP16d;`@CU$~Q(-Zh?rDh?PhfCFXY&3Ncy zNy3Fs*(8`MhDqIuPvj?ac8j&8q@+Z;znIhz*T-|j=iVnyy9@^Tq-X1yEl5U{rBG+i zo*l^_2|~%^*LJz)gKjil2(H~IM960(07N3q;236feKxu27S9Vb+(4x~$$ z+#^yi_9z;PPYq5EM4~+@Afjeqcz`i+0ABTIG^9&skB6i&tuKBs2K-SkcxC%J1$qHn zc^sts;3B|1jfTH*5DtB@x%nP*y~?&%`uW>`hf-ahq9u3MWP~~M1kzo$6V#87V=!!(NQyJRFpoCO zr!|i7DVan}E}RC>e?5}j;#Y|Jf=QyuFx#*7#*7e<7rYc-B|1S@Rn0 znj&pLaqNlQ*+!LJ%y?B{CUnOpwc&vr@?Q^RbEQH0$C}hzvPI3fSHg?PW)V(;@ENoQ ztC@8=KQ%tAx@zFq>`^s$8~p{2xEm%ebSn5CHLRt}KVhQ4APa$F5(dB+kT4{9HZYF+ z2l_k@+zbK;{=H6rQs=!PjI?q^AV1ghN2lxtA`(0vBKuV2uSt_WQKgN< z79os*hcdUGi-m?-ag?ux&b z=tHyBTGf+(tan?1>Q8^s(|%CG$i+ET{E!D`El@AI7PjuNYvjlIQRs zs2BLRoLIQT&wpcPP{Ejm!Mu+M8!3w^X=!GQJoEQ7a&qcv|DoG4N(dsV*2u4~AF_Cl#{C%B527TZ6eZxck|=#0eIz*OvGrCV2lxBlw=`Ck5JHv zl<-)^iI92j)r##&NlE%k{)!s8Up{j~1I0f%As0d$e3_Gc=&j4eek84SzjEO*m;l)0 zZeHq!cLIvGAKJBq;j4sZrIXGw=$s#K_D$MW^+b8EF)Y^9)3)8}d{v)IoVc_ow9l!4 zaK&876A$2fj4$=r*riOpwVg)Z)niw4^TODPv_$AKv?koqQT~N-tSHp+Z9vftw8tx2R%dzyCpcJmKxeN)Ho?D6b-uy=JEdbFFg2fpsi$Uv-J zm~NY3?&6HJ(cTlzm5}^Z2^YPkS=kj$o0s=-xM#UI9*2~fcHJi(+^<}m zThD-QBy~wtTd;^(5q3;fP|QeV>eE4th4V-mLx+Acv8YEe&-Pu5NB45R0r>e3t2Avx??Tlp;iZ9*@0(4MUU+ zRQX-7X8tE@eCe%cG1CgnaM=K&NF0YKqohg)7+uG1C)V@UPQH8yB?ZHg&*tR;!@R3) zbzB-hm<-FPjQZ;Ubct`gwAHZ?bMjWJnLxhNrKe$9>8a8ZFvgBxzmu{ zC~8l}oF>p1aLLEK`HJi#@GQJb`usbYKYwS^p{q;57sl{=mBT8nvQr6VL9`4vi8(MW zJ#yqoZ+w_n@b=q0BhLM%7@YAPVF+yS&L=7=>fZOhMqba8e!gw@ONwU*OhtOr)z~?A z^hp4V`UISn$B8cvx#EGQ*0W}XJl&kNB4OV9(!_l{Se&IUSKbEj2uyhwqt)5NF5xy4 zyg6D8X`uO0>ME|_Kv^^_zu9jJAx&SFhqPCobIY~xOL+U1R*&+^Htiwr|IpMrnf(_& zHt*+6vKXv*$}Z>q=R4ov*)o`atZT^Db?+)7`9^eZpeo9Cy?NF{?`) zaZ8%~_?Z3dSvzEh9)%m@D2VM>M*R5RW~38+{OAAfOR#r!;-qzzjS_xwT;sh}LjOZJ{59XbA2)safo97i4D760x6drpVz53n zE&3+fgno}2Hfdr?b?&E^PFO#O8oA2JXbo!`pegYnouAv1-^w z>?ORVViM0AF-qaOYE^cjh?nz@2YX3CL=45F(-qY{hO-hdMIdAQwZY){@!xFD{U{0? zegP*KXClQ(8FgLCwP)xxuF_;|dl4wK3dYoIn_fCP4KU^VKpNUySR55I6S`0U|AjB& zr_4Fn`OSpEAD7ab3wQoB?(aZswI|Wcon81!SXS-f8rY4E0eUP53I|Y9R+%d|puv&u zyJ-5?R+VLoqH_8>R7``2B*iu;jrNgRGgp+skrb+VfEEnlG|Xe#R5uq}NV z7i0f<` z)VYk8!qg_8+5ctDvvO9A;BeBzmxar?$wHT-HEp=mz&h6=~^IMddkF z)!3+2d4nB#(Z#vgJm}u5bw4Rvl$~NtJ(g%hs(UDBy_6 zPG5K(Ar-cMS(2_pVB@vn*>?XD&whPd@Hmp}UjmXEgjxMItS~9$&Lf+|7ROE$J{{A3 zTbsYYQPO(By?=$s{}<4bIb(F!eOI47yL>BTsG{J>+26R}N8K)c*?5QXQt?Jb<5Z&t zGf*TIT)jqPB)U3c<@eOD>dB+P=L<8s2_hPJd$mBMRIfo^m4na|o{C)yge}WiyedmZ zlLxA#5Kuv^=3tiUzRC|GttaakuKi(t{1dV#f_zB9F9u@DrRt8Lf{Qo{vu?7d_^ZV zPXMUh0OFAE=HO1?#;+i4q-<9?TufCmR?#!?5+;)^VNx}GdBL8vIQ6S-L5&t%tNV3D z6+1vC>3pYx@DML3WTM_0jwJO&VDG8sOet7LZg}{cLr~hDYJ6-sTLwxmDD= z2{46;wyGC z)N#q{6L=`8_KTjLThFeLk%X1KY?jvPrL41gIs}9fX=K}x$Hy7}T=L*l%Q&n3{v+f1 z`PNOiN9u-2mRQuD{T+)1CSOOy?H`|CY5~Otd|sC^6{!6>(7|pJJ3`=#C9j<;a{Qa; zom1nx?#lzV8?@|gV>whIHFD9DZu&fRD>d%qpB((UXBX-JdSh+GEKM#XkqCAF0$N8* z4{!fC{FChpEXbJsWlhEMf($Tj)9AeftTDb8Ewc2o?-{)C~p3TD^>R^SEq4?6muRoDymRlpLxUL<*&Bcp-gw$=^p-#>PPMG zpI@=uqNqOqdk321Q+Cnk#4-WoyJfiHl%f&wKZp0$Pl=vQm=?3TY|^a(k=MFGk)dpUt}gsVefhL92^9f`XY| zT_PkTcU6+>cB(Y#o<6NhFdYvvLTQ6E4>wo+n%aLul{LV1_aoH7bm!RVErK zF{G$nym+xenFzn>*|(SX<2@I~lVw{%+QPfx-G$9={B(;3pP) z2(YyLS#Fi_t}Oa_s#rieY$h?lA|#ZThlj!PWK7Q<5>_>E{{26u7ZSZNBid8xjmnhE z@$o4H2m_i(0HUNRo$SjyEaEuMp@*$&S+tjY8>UpNx6hD&NM8(2<@4H=y(^@IHD;?v zvk!!rw)|yGwX?OA%$r{_)oIZhlKMk~TU$X%+e&R*aAlMfY76H`U@$qQ#b~2oPtOMJ zar+fUd#@hy71;p>G}D>CmtUC|@Fua8goXy$;VF1ZdJ_|bICZ~63&g%5OcxF3OmNsI zSMl{`sY})VGR>pnI81g!J_clL*|ArjBB)ad2^& z5oT@)u8a|bE?bqJ$>!S1eim1Prh!YeoG=Yn=k%g!+uBa@gn#H2LoI{MYWn&bWv_@ zNdNZVx%Kim;~d^g^bfZ#6#5?@#F3}p52iUydE^iX-YzM%G+O$+WC4XE;f3sfEWFLZ z`dUTyV_p4!K#%<&Y%*6i2^>Y78-4q4YZE1%2zdWBPCWGw#QR;mHDJfioh9Ux6huw> z`pvx6fWh3iU;e9j@RO7^OYJUQyci;u40b9pVovcg8VN6z2aJZ~!$|&N2L~k=7njVc zG0j_7G27=-lvm>lcuG8aWVQd_z5mesbPtp`KIvQTY+UMmEY7=c>%MavgJGbYwfLAk zb`iFg%*(5mJZFg&_%0rp2XKJW3s;S; z6--gNmpo5*;D68my4R7V4X>$Z(R8NfRz!i#d#aRwAAM(A3(~9?|CQOQ{Os>|`~N`y zVPDSEvRh=lfYMe*v1@UJCt7A4WjWa)_l`wTG`4)`X4oT#%z{X6KS36ws4lnAbCE`RzGQnQP{?2IEQ=2X-?)Cn}xjj}jZYAc>!ai4sk6}#4;j95Z7WO*{> z%Oc6k(;|tzTHO~69W&u#P`~0>2q0F8wz-lVt_~-kwtwmmHjbxpDXRMV%_fDDl{QeP7cpu{f&+{1XEbZ zxH%GtN5Aaz9#(hD=_Xjok%@80g#mv_4Na`6UgL0yG1@G75(ZU8@&l`~(avS=Rnm#+ zxOiMNWWkvl?x1Y@K%GG+bkeG1+s&i$G?hw2=EG>uZjrQa1X}h?Bwkda4#eoWzbCux zfck}%9+%J|f7vM3hc%|fSqDleR%9>^C~!qGXt=yJ4^B-};r&;yYd7&wzlOYF>b`x| zoBBRjzVpieM1o|l7>MXDl71L2_nqr~@a|c(b2f$VOM|BPRouhpFTm+qR%IIJ?zYms z0r3*Uh$qvOz#YCiIz>P z=+E@BJtxuE(K$-=B8gM;*Cw1Ai(sF&cq4nT>+=?>&&;~Xx*6_cO3r;jiP+bD@J!H` z1E z10oaI$dRX%%WXOa3HoGO)u~fQ!q5tyU5HNm!tMuggM}5ZQlz-dH`&KudmA@ae15*Hzkr z4;e_E0eVV-8H8y!ro+g@U=S4@;9c=);E(e1T6?^2E=R(0zKs58GXW0U!VQ_29ELE? zlXPj}Zh!PgUSnF$OqDun;YBVN{}4YdgYVY2H`rkYd?Tlz#4N(W2R`}s{?q;AlQcO3 zX7*cxo;R(Rx8hX)ClW0Lj=@@u}h1bP1 z;ShVvGGG21Vu3(@2%cxEVy0(%(OkRWGd@m^xR!VMz#WFyR4$Y8tDpU3^TTLNq~xPi zl1`^pieA?J`w9h+N2?FG!<(b(I&apFwp-2jjnbEly1jZuFKyS014$Bsc@>JkeQTN0 z41t4~MH#V9L>_IgwKp*Ec-#w=c$u~Drm1XVaI<;0=XZFa&Ipy5=J4ss0XQ*uc+VcI zTwT`%2M3$2K|Pz2sDp%RBx?y&LJ40v=`$SbPHj>fG9CvLUFLt=ZS~JD$0V$AK_Nq* z*eg>;{s~%<(g(!_!6MdQjn2jbZ_1-?yypWMy*?#9BJh4S%BPmUKX;GGu^9LMwzsCE zN4IJd7pB*GN{RW>ysC%a%j|!Y_vir~srKi4G%U1L`&Wc1 zgujlF=%)@;wt$d3^oE?IV;+3t=WW)qWj$m_nU%;QEiR_eAx*hA(#gpQ?_HO^eJ5)h zoGcz3E*kYHhrseg>B(vhc zGDnLS?CF^>O38^P@#kV*vJG_V*~&r)5tYtt&ahLF`c5O@0$EeF$(ohj3Y(gvpT(9BY*ek>+8^KU-?$+!T?}cCPf0J#iMA}?engp{Y%Dfz6%Ow%jbsM(w)|PEm%)$-JsK@a`VHv4xV*PHUn0|+d4lu(HsNJ z$&&MDy7FkIg4JY#!eth!Qkd|}jC-ttvoX`fc@B=o>vUm9`_zzGD{^2ycjxjo$0`S! z4So<4DcJkhYg3fb3XbOamAM>NQ?2_CE6vo?I)|#mNuX!Ns>RnXW{1$t%&*)rDOiLB z+heN&>;I>uf(_hA9U#eUr{ z+aLeG^)r9}3uXSA)$KqzX}*tJjXyEK^~x25$#VBdf;=T5gcy~bo#v)3$UeQ31{=4T*@5HT4v!e@+>t!e zIX_`d$*bH*n_+$6c%|ziH;u*TxjShCm%OZ_s4ViXY4P@M(6AYJuCR^JjhR0STB>*} zF;V_ek&)+yYgI93yl?+ux^0@uFg8beadrhh8=_#k56qxz>C)!(T0ZqNw{ zEONZC0?f(6@Lb)HihrNv#NV=3v_3?cj!=hDkoWAbajZhX|<0dAk>yd zDXNT*+rytWJ~uBfG_EFo^_MpdE32x0WdnYA_T))Koj%wi13emx4>rJ^CnwQ49;k*c z?SQ_lULaISIrSD3Hb9zr!|Ow!y`NURKzMXYwcwP&E?RmSN1j$6`u+E7B%kD0|Iyw# zbgdVF@@qh5(_QNmi>!}bDnfTB@e<;`lEjY>;}Gwo?cB~~6nIiM4{_|*a-H^m;gyGa z!5}i`$lt~C*zwN`XV11sspXp=UlRz zN|-80l{6x>5)Qy`x(?ru$?gaA>EjOEOHc0=Tg`n~kG2Z2r=-T$?$~rc@oGjua@3Ko z%08B|HQ8sWnb|f8aYam4U-lp%kxn5*f&o~vp+Qr(^Y|zmUR2z&*YqZ@VS#<=)2B~G z3Zxr-H5*12DyU)6C;ySBsT4{IMx|6FBvwyt1-8A|^)FYYZYTG325Xm48992dQ3!p0 z^0>8nczMltJ*t?b{%2kcTyCIF;?Wt<5!%n@v||#4iAdng!Y6%r9A48$=oV6>PyJa+ zVh+t4p&GujTaqGhCMKp^)}A5|AV&nFruAC!|5q27DfS5P#nziHj8!5gqxBZ;s&wEq zlj{46DDr5l>M3jmye(Mz7kGQVp#Me3q2)^n6_P-j{Hh)uRf17-1g8xS>GsBz?uvnj zvM(7Qk=*06Rf5VE5uR{zvcdjLnzz=KXj)wc`si#$x&!EW|q2r?bTfi8t zu~o`x19Pi7$mwOTK>lPeQX4n8Ryes#+U*8Bjv%$su=MrV7d6Z$dbe#WS9UNWVlfY2OS`^ymAmN#24GvVzeCH!5n=tm>~z_9A;zNbmo z)>ZbMzQVBDs%?Bd74o7~HNAdxq7B{(f$c~eFS0JQ#Op)?09f-dz9&En*q9@YgTx=3 z1m8Z@-q_I5>8RyIFI*TDX z%snmGUA<|qfZMa#7|~ZUBP1j5$EqFoGrqR{Nk({U%-KBIjge^xE(v;alNUfX?3DE1 zIegcV0cpX%O=F9jj_lt6(1sTl+Map(6}X$w+t9>jRZrsewkfau467hBPQsd1vcZNY zECsU8B)ZF%v_}6Ga<`>l@yd##t2^*sI5;#u*E=h5rYzg~utARnYboT{Dssz~9Qc=? zjcXpzD_>CuX-yv?(zqEB9W)hT zn->LWc7CY6cCsXO8s)8oPxt}%<&&S5I@A5IK%}VVPkAa5 zXzCs(tQk0Jw}#!Jk(OFeU7?3rcoh4bYE10`zmk#uD(AYKwDY!6FN7u2KH{< z4E-Yudr$G2^_V0D&c%QqU7FOy8kL-itgCZXXEj^;f6H=bL{~!N6mQ<0)l0qm;DH)HR;%z{winm&54Pr--gxg<`z(lY(N6-DD*;DObAXQ+o4t(iK%Xef~>i*}OmpGRGu9#4E` z(MFTb=-wJhaZ(*W&g5&?$N$81Hm}Z&54}9O>uLK|MS)8*{vbID_{GZj0P<_!*e@=9 zIc2bnPkkNdzwvxP&SjmXx8u#sjF&GzXEtb;NoO7sLt4L$bnh(-t2T87srdTx1o(#O zBNaxjv~Az_peISWLf7I%D#?OtEtK5P><-KE^)?svI?+pE)kyO&w0Drp{#)?c4Kxc1 zN&AZWR`d-xhxcR)!n95h$&$rQd{eg`hATg|4xe~L*{FSaDOntXfbmsU&z5X8>AY~p zUy_qBFP5@6aGbijx+*%V#9CdsX>Q&$rp}uYwj6{F8aGx;@ap+~b*(SON!Utcx!Qul ztk+YES326+RV&ZU?w!g=h{z*%9 z5)hJ#7!+Ip22@GZGZKSD*vxC7bvXq<8kGn(Gyre<+NBPUwBW50;Kw1aNox6ao#I$m z^Vwr%RRPCD%e#f-<)GZrsbj+^a?0oTlb7o=3|&i29ep6~>{%&6`e1kxI>n&aEMb4o zwKbEQo}1jth72ipsT_lPMMg$4-5cgIR}e!yL}Jt)G-=%UI-jiH=L=s{bivNYFFKNv zmss=NyJj;3WJg8?2?u}SO>E#AnL_C_Q<&XM->q>$h zN95g@Q=&GW87`@J%Kd+vk_pnvH;aP=|%WLlcLt%a0gj2U=O1D)r$Eenw zb+Sc((jRkbpWgYPmwm~S8HmSR=ZDvLpX>O4P5fWK`9yF^XMZ}N`kdJ0XlQZ_iL@O- zmQOwxS0epHi|X)_(#htw?a+Lh>kI~scmj@^k?6T5^9sP|RrZu~u8bKordeGhLODAT zNyHOdUm@}~g1%a4?C%fI%@kF_n}*bPvI=v)rJJjm9X1D=6&fGCAGBu8qb`wW=nIF-YBUcoq?m+SNuJM zv{xB~>#1n$5a5tm-+0|9w4<^qh)mX2ScP=TE*Y78)ot*1>@ zJMa5?XovgL2hupJEeBrhuJ~f!$v36b3m*GargT&9LWYh^{s56j#EV^YZWP_k=yN&| z(f>G{x(h=j!8?<_&Ya-DbpcXnb=L*?`>#_U9}J`IV1>JW6%Vmbb@%czc(4XpOc6IB zePzpAC#&8>H2jCt=?K~Ri_bu`?pR+KbuNDv)QP5R3tsSghf zWc<3&tZAQRJ7#w?8`(v5!pWF^*0C+0+DlDq z?9TNpbb60D88ICxLpM1mwY;h=bOS_JD;x;isX*>a?^+*_L@5yX z-m;-ou!}E!-M*c9!}@bAi*XZ#B*}u*HG=wFzW5yx!k0cjZ2Z2qwc)^n2M)+%&-nT{ zZc#fXwCf%&E``RIY6y1puYCR!Fn3(%D5I(E`&Sg{VE_Vh2m#A(4?zEY6{Ic1 zGD2dH;Csi^a^-9ZTo^p#V;?9|9yRq+@*ZS7)yhtM(&Z0|+DKLzoIAaH^5n@kJon$p zA5wc7fPkL@yUCFTFFDxkzYIACx)czg?r)||4G>luXW5L%3mp4J&x&64w`~S$p_YXf z;TK@ZdWsT*rWow;=tO54`53a*tnRx8AX=B5sIzC8@X}ti_R4CzSr3I3i0f zN5g@Xv^g}{;3BF@3DjZ{qZ@9isPDRWb6T8$)sj+;^Wjp6+0{vDl?#+?TCEoWun;-rt3C{ZZ`VujVa!1Li`^=L+gQ9k= z%7K=Zx3S#rEe*J&VKGcyFxq_wp>_t0k8H^06?K2v3&IfqeL5gb=I=Pd$NC+ z{>fc-b8DVlC#MxH$NnBOF1mJN*pQUian(BaFNe51|BzdXM4(hneahte!Hv2bP5jz* z$A7JP&NuFG!B*{1$FRV`b$iwPRQWMlX|4U_?=eGaJeoHj7?k|*>DQf^}7c$(;D6$&`*!Csj%bpSLQBgK#g7rnINmh!aapP#qESzhos=BQTNZu@Ra zmiv*^Wo~ZG?H3HA;e4}nu$ppR9xz*5+g!}l;Ia$qI-FRLjzL%r{Yq67fC6fF=s_D0G3wvTUW3!w|Km`U8E#GMlYP$}t9`3H_Ttm@2fVC*$yV>6AwMubr&xfQMz-p2KXRnQ$*FhC$3-C(#MRQuet`xy7?QB{w-4+7nN%48dE*O@swteTrZpl5&UcMBoTTY*1gq z1U6Bz8w`1X0H&~ilQB-w3Mkdul32#8uyOb9-GhS4dvnsrph5l$rZ{z^O}IQn7`%}OU**GsQibM?9w-~R|s z?|yWkgLA^ma*e5>6yE8(dIuezz57~%SC!3p7q9)ZWpF5YqCDGk)4SOSukYu!>dsaqe- z4e6i?g^l5F#C$pe>`M*28nI(XWBAZl=+5fySbs`4jdpOSQ+aSD6 zhr2~TZQ~Xm)LJgI7G0ln)yGiFi0e-1#Tm_&$UYRdz1P)swn!qye+=5W#Q?W2`AOl5t4No zu=x3b*TebR9>t#CWgj4_0CSqsvF=;A4{Sm`rcUh3q<&6;AbNoY^%bICJkZ}||Gj6V z%Li6QDvzn{^5pe&o`V)efiQdwA{pDJ^OoB8iMG;RF;ml!L2~E2uI$uNeT-sQm*+#2 z0O)vru%>IL$D*XRK0M7#UD zHJdY!8I2BK_5xl65rr<)9zIklL>5B=or&aonfKkC&2#3?efxeuX$zf4YjYkn9@T29 z!5~0x5T*vLNeLwvKIH{y4 zLc2clD4O~^elRM8Xi{1g#6;k;5?*JkT{ZZfe*gYAc*O+17L74YD)CyIj6;8IJmAk; zl{+0D+xKwf{zHe96*k!$AbUgv1HGlSyN1d54AmJ}RE<_3z+`-mdcENHBlp!9J{$di zL)+J_8I33FDNiuEUiO>Kv*+G>)#DciUGsK2uhWle5c{p&Yln!U%m4S+fBoj`R8Xp( z;j|2GlK)T%Lbd+G&v|0K#;w=!SZ^5=$XXKC*XTZ(%e#hHp%;3^E*oYc&L0JEy+UJh z-;PURCxG84#t!e?*;v(a-|gAF-G)p%BV)$*z0Xg*tJaat(Af?^vm`EhoT}>4qlZv$ zV^8$C{4ul@zW{Su3bEZF=tTP2LvI5o2f)KyzW?31-ZB|{6-JaLt!Iq&^L_PLjVa)6 zZ*}fyM zBqS^w7JlKvg{j3_Nq+nGWg>Kte0Q7BJI|NDzi;YtkSSAaO(1Sg==t4;^W2`-`o0J} z%BK1Hf%6$TpqQ#B1}$Mlrk_t+b!FeF1xpcak}A8I8S|cESJ%;_M}K6;{Ph*5IG<7X zjQv~+=Pk9B`N;wKpUu0WNT5Y|%oad%WKA+@^Js}0t850F#B8cNAXMrpxcf-eiwg^?c=q4fpz7?SFJ|eAo&|k3f^u{u|$a=sj<@9SQXk zQPp{Q#LKyynSL%|w8aZv%;ez7S#94xIv-j3@?A6Z0!ei;I^61RPKOB18Fx!Vn!3AS zY4qE&Yynm0kk*3Z?!o=8jrut(_>kd@XPog=;2~WqJ{TH=PCaI(-h0v9x%C)I4-ayy z#RRm^-~z*4Z?{(_h+PFqTg_!@m+n3jMe3Fk8kJc94L0dJX(Y|UHZKr_Un{bnbE;lx z+l?<`=WIU^cjnCM*XeaZQjM8l8?CMBUlhoeQp(1#0e5poocQ!IVTNj?9V>kWBfC>3 zgZ=(q_CbYJe`?ffN*-+%;@a$Ovk-8I9-U)TLDVA2pXdG_rFSX*^2P5; zFQE3eSY*5J$ZFd0d$H??TAA_AUb}cjuHpH6w~}Vsd{4nMnN<$}Zs!(0DJIkw2A1JVM_`d|REn9R{ZZ4>! zJG8#7f8^zwS`98AQIC%ddA&Q=)Yf~0X=uY&gHHGVpA{8fJGDUsz)z&lI1G^Ve$5~g zUq=u;$J5_<^z=5P&Uxf&dQ43!+?#<0|9%UxZY?H$GuNFq}~ zWoO+z>bqeJMp6W|@9^O3CAP1Ka~NzVoF)fpCx#N-K1Q6RsB;_9-Biz8ytpZK3FovE z&nR;GW>|Io3*7_(-uhH0z$_}m*2qY~s)vqm!DDsY+kf$>Ieg@ZX^(V@tQ=|FYWpbrI4GO8!k}6vXm=Z&rj2s|EIf`r$?lsICP>Z7R#2%sYxXq6VwZEK&Blrd!p63y=LF~ z^-5ym=d*MM#Rp85b8S$U<1;HmkhtMRUG zq^%B-f!57!Jy}UYKy3YF6?N#E{qJ2nhjr$yvHtSpN&E^I7~JMi3us>YczpZV5T<JBd1R+x>p~;PS^D z-n4D4?$BgLuc-1rcyKEy12JWmv9{U$*+r)DCA zrO(@CLo13Vu8TDE2yyp(eqo7yiC3t(j*0UyDlb2=<7Fx)Yg(+{Y5qXPERJKXqM-{w ze;jQ}tGW&wX7}fv{G}pf_N$`0Z$x91zuNE}Ys=i1+^$}B8)U^)s~4RrZ~DIHiE|4E z5=JwAH}tXK`26X4uemWSe}{9E!=m}Ho_}V$j%eMw^_ZE{o_J^faq07fq;VZRW(-{X zJef1?OXc$gEC_nDYHa+g%L_jI=%hGeF`GGTy0!JzY&WmkH{v&B`l_pN*JMHU_4t7| zHqw(Xul729#(C3by5S~cu5F+leH7o+s+3#0!?bV6iSCKR;Ymx@n6l3L_UjU$-Ab;X z-=`JBW*R*zN~N_rR^}D;(+lmU+wFM|L|Flx8pg5$znn!aA8Gz}Tcgh1JDT&|TkU@F zd&=zvgYBXNbS!FtE&Z|t@K8miTTq0hdjZwY^0=bB;p)8dJ#AW5Hv6YZ)l7=fu&yZx z*)?@#by???)qBb`O?+O|`*3SnQ@fL|51f7!{%X)ae_MX1*%raxprJzx*R(eAy-7|u zZs-wc0^EoZ1J_O_JK&4<6cD%f8+d|*gwmOIx;@JTl@;tgwh0PbggW8|R?YM- zh$GH|Y~{G~^Rw#F6^=%zTm3z$CjaM3ECy~riXDJ|wmbXaY=lwIAi0K=uuim7KpmtY zjG{X^pE&*0TcS%SC);~nMcSfHsD1Mi9~6nQH;;m_j%`vv#3kirE_5s%b#;|_>jJKK z={oW73H1xzwg~V~U*b2Yy#m>UBaJClI_r`>y{SG@3Wc?O7=dx*u7jTbY!_TwSGJR> z^_tlpUk9}a1k513?ag*3i@jBO!PJdBzVa7|WmaKr|;_LgUBJUlvcqm8C6T!grXl&i#x8kRb0vO)%3Nyl%2 zck=7*>JbP=%ah(bcm?Gd_P!}xK>gaaYky>Lx6N8$HgV!89Ic2q@VR)pg%im)x4FB5%pcXRKGuw!3C(YMMuS;@;~4u=3^vCJB|%F$1Yu3>7%4 z*?mf+Y{ZEqxTFW@Nn_#;+X*@xi_l@D1X|t(vgm>!P?E1XOv!o*ogN78%_AgWlR{7r z8C!Ntfj0+0MP;lF9#LDtUY!&;4hJY-^>#^~6$4IAiw-I(WrfHLx1q|uwZt9Aq+mgd zfBoi7lK#gp)p%HOqGq)z4~h63si0fmt)WvAv5G*O0IxUcwQrPJ&7Q55i1ELY9>y>C z;j{aV+tRhS19Emlege-|mn+qeDh!tyGa}1BZF)59&=6f@2sDsY$VTS^zA^QWy zN6Jh}F(W9e$ID(cj3uyH+t;<+Q`WY3|Ng$fqoAgnc%fcHo=JipB*wBzyA9>Nr)4#s zG5_;Xa~)BP2?sC!G+zHRtP#6i>O$lA`0}dgA4i+eT=+7p3KZ+sP7X+9WeUNzHaG2H zOgnULgCBA?KSO5;D!T32(;}=60MB|XtGO_gvSkGH@XO8M8s+k~dMDsewq<8g<#Tg$ zJ3g9!(`1FGSF`+S9v(wLBzpzD^6~XAjftv%aR6zB0iOlaU1Wk*-hCx z?!psPjspO!9P18{Z&Ryg^Q}x)_HdR2nMP(cflVc7~j&j+7_||RPCT4Z~aM?V}q~Yzg2m8$V z(!M#ejt$51pH;1z2_|L2mn^eg0y;?Xmeom$*hGDABGE08gxk6E#TF#Fa=Sz5Bb;BvpV#_u z5!6oPDxi4))T$1K6Bs%4{ELG?)3vT7qVI3MGHW(wL7I!^GfT<%Vp`*c?HcFy*C72U z5jaHMEsdi_bP%)!+!uX%1;uRBeR7&#vfZVuuA7VY+%LJsp0cwR=vtL#+l@QfC5wA) zifRTieI2}r25Y{Fv)kXtLm~9r4+12W#&kK(Xe3f{cQ1$$lZ%D)o_p@(o9n2V>jubp zaq3j}py8gnaTYU@yb`vQN%|tM-e?j5c)*o_LZJc8R4c6Pqu+jCTmIt*5-Jm)jkE}+ zWAuMJ^!=yM0Yml&&#jGi9jm{^qayMM15Z~IpQY*DDT0wcfX8T2#1jQ1Mnhg|D( z02|;qEg&T%#{ia!k~DH5;WXA5KwE=()ZVkg^GKlK!dgbEQ>$?HUOyKa+6i^tCiLNo zWSn)fJU6NeBnN1{0izQwr<})~{WZ~TQp!Z}fFnjWdA%8Y= z-!FAqho65+(-I0Mev%ZVSHsM@?i>5)At@x|StNNr`GV;m`^m4l^!Z?amzEeCeCYlp zl7mOWm!C%R61m5MCDWPsrDyR_=C7^_Z4;5Tqqx?d%FMHS$n@R>B*rEbq&ySn$P3-5s=B_b(T8}s>juYn$jjp+!+ zemQuw`b7@mLTHA%|4cYcBsE{DZT!CO)!e?GCzOMR{caPQ!6rYhK=J&?3ctP3DOJ`fFUc1DBCL zcV)*!6?UgMK7ICVM$Wa+&ewsX#f*o_Q&y-)ViBjgLu;es|5}Fq4Ofb+PEBnhYG#R& zvQON>snU_z6Tac5*yW+UL6DAwj_Vwz;{W!%4Fk!F#YcbkmGzkCAHkAzpxeA#HUP#8B#(^DYb8l~^PRcAN=5J7#eWG`XV(CLYOglq=ZvWf*8mn6yqGx=dP?iv=vIGW?Tt896|vP9uQ9)BMF&F4C8Hw18WZ@D zKIz54U03mtE{&v`cj@2%ywUgW(I^Og8q+As<0&OqDYMZg` z1&*9S_OCOq1i_7+(2D&Qej>tFZ!{r)1SToXt{Q)Z)BpCGnbLSN+(?Hb$$Y4os&57d z4l^*&Dg{7y^k4+M2*fqxMQX}gK;F4OCRCMAW9A4tkO(vo`dp*=9`)yi_m6F#eb6#O zq}ky5n`qR2t!~PW*2FU*-Qewa5j%HII2}CUkoV!YT>Z{n1>lq3-q8-pw!&y$$e>;i zt~#G5P~*dn`YI&rB9qwbqjxrSNNcD3wyq-ZRRh7-^=+0s<#FkL^d@! z1!UfcpBE-I@VYAKf-J;iGH~R__Zw6|H!B`A6iuo|kCr(C(D^+>)Y?a&HI9v$s_~>i z#T=N*1QX*_pXW|n=^=u?E$J>34i&jD&dPa*1YP^dTy(<-Ig))EwrEDR#iG;^Bi_5g zX&R3SYK^$}cr7oMR;L?cV@`A8EsAQN8g}RM>W}LpU1M#&rBA+_?exv{i(Ii7|~SZhLxnIFw8K^|m~}c{jJO`kepn(h`mxF??`i`tHb@5QnVWb(Vul zbUr=Lv}c#ohF8NfOlaPkJ=P8qmt#4jcAhK{-hSlJ zqJ8vf^Z-h;O~=X8~X4~JRA!W`p7*&KWag3wH3%8}y zf}1t{Jj)(VbF}GO9X{9DxdF%#88;JDYb^j_otqxClkt~y_>_!{X7CiFIj=kJyRFsy zqBuw?_ua3D!=Q>Vgc*6JNtp6>BuC;U20h;L_y|Q;P+IXO07B^4CmQf|GU{+BU1MoG zlQs`cswnx;*|bmBRB+a+u(|+$;fcw|n<|o%lNC~{flIq@{ppNL(u8S+vaNfmC1Dje zmn2nf0;Iln`*ycf&sLZ!5H`x5wxvg4b+_i`&ce^#9(PN++jCaM{GxS}rEPe(^}>aL zq-=@S=Aa_olV7}8RlcOQLa|OU)?|cL2i~)^r%&6`a65W(y{H7*nFH)(IUX(OHboRl zIog@7kAere8IMQi)kbpFJQ6s2N#UYnXcS}f zZPs#!8ykfldF$lpNbu+S6z^s)J034~b*LZ4`uI8udhh%oO?B$2P@jh5li(VC#B z#*G_`^jvYPyvl)bZ^dW()3$?4E~yh1!BH;%9z&CEtDh4Gm)rQI~o32v_Ma z#0N%AmcKz=cz51Um3p&%D9NiyJD|3Ci z%60>dz5jEVY#FYF*l$N%!n`(Z+MrEa2dAC5+3zT{9R9`*3UO77u%%q%{%@gtGQM1) zz!Bx*nfLpTAXAgC!=tX8X^!w_?0&m~Rnv2>-Txi`WS74X>1*&9FW)a3wz^nB^Kt{- zu6g2GcX}ULl%`oZZ&&LfYhu&tI|9D;43BEg%&DJ%K2=eTrGML=yBk+mi`IP!JR?Z{DwFo)bACgWp->!RcI?|3pSZi7%+Dw_+@r zH+(rbp)aRJOJw)NY^IQbGz(MV*6D9Y9kaZO{Eb20@u00q(HSWkDw=k`gA8`akZ@@FtrCYa#BA^6=k!2#i5`T2g?oD3-OR0qBZ_LDrn+UO* z-40!rBsmk5D_OnFRNI8~q9cNr9Eo@3@c=}EP1^P|!XQmRRAYt|h?&~7P}%XHU=KsNO@80zoY;u`4NaBH1oU`j0UI#Va>w9Ha?y<6-mt%jA3R);!4}zk~C`>E# zgE_{Pv{Om<^Z6!u!3Z7Hp1NG@NT}T|I8?Kgm4|j0ahT=-zyfPK9 z8!(~Kqj}Fce6F8wqL3a@Ts{nd@H=A8X3UaYy3UKEEF(m6C4gW7Z4^OKwG@I;aY_To zo^M~21>7hyUg}sEbWb}<88Rhnd18>k!Q;!8K{Oih1_8gWN5Dly5*6~6?f3U6;KgRl zkZ)G&_|IdO5gFLZU;&&ee@JqnNzk|MA5-<{^<}YvB9}?i&m|T!n_JXxfpRL79{hnQ1AG!KHgj4%XVuvr67-T#I*&hZF0J9yOMRnT zyq!`_nKr^|YR_@^SNt|&Dy>YuEd^AId09;HykrWV=~Bj(Hu%PxNOQk<>pL zg%^9RiK0+mkebQlGw`gnsCsWbJj9cxSp;oT`DL=D7X;OIx-SiAsfE#8RxGF491Q+U z-UPiJB@4yhH&#hkTup0Bt83wc%YKHg0qUxlwF23sJ6Ni1hd?}yx`FUoX)K2Ud%O6Z z$l9shkmK~@SVR4e4e)Lp)#2~``J-?4@dhe3$?Lq5biz8c^!%j;nG?5u!-my(#wpWS z!@u@ZRZ%gn0iQ|y7!*ay{^Vz=4nw>*AWg3d>!*Ukl-PsEiA=4#7yKQxy=`B4q*^SX zEF+McA|28Jzc@c_@6vBk_;bKB6`@Ag)*i^#BBf>u6>EU&;QRz+`o5vj)G)E}!*IqK zQk*jJ@?eM}aC^m(qaRtB*&Gwwi2j`^0E%#0IRTuNb;@|YxE6{OoLbGQY*W>#rrR&EP3m5v#wI8vt~vJWw=%+o6FcUWw@C_$kd{PN#4@w)!$AUN=?~8` zpS;^T;?H8g?3_N6X%ph|rOlCFj=t0%nu7(Di#@n(C;NYd3ub6z4yfumPKX@H{@C9$ zb0;-lEJ0V}B~VrY;`yt%FXii-_dZ8NC zgx1UJfBO9S_W0D=&adnZ2aGY|Y?DU0KnGDsi0e=ga6H~ZJ->X`UhF~t%jrM(bKJAH=3d0k4M@o!>K@@b2t#n{| zj0dh1D%qKDb%x4&j;U2TJbAt9Sm=@Ii5gG-H6W3%?6k}8{k3T`a$0LQ-R@}2PUY7d zZ;UGhQnhN;Qi{{-3T(K`XPwE#r9e^2mMyygnw?XUQ~o9Ji?WJP!l~~FT@RPc$k-ln zhh0jY1V6`y$!5Q=^t9;H?uE&!QV$dj2iu~N`I{Uzw}IpChkH#ImWBkwtR{zRZor61A|Qa}E3um!(S@{ae#89M`8glj{@u!0Q*E-9mw8G} zyDcGSwWF3zC>j67A0+=~-X|EohM21h)V&|$fsI<-j4joF>P0;g2G;!8N}51n!~4<{ z0Fb%tQcTr&zIglW0VWH779>fc_)Qz{)6N-@lUAAp`HvN1q39T4Hkz@2QFXtYyWqzY z)~^OR#{KON;0_p%Ovr@X&8g+~H*#Et85oO3g#}17TeWGEOj-a}U+x-^b6b=WFhRUJ z&!{q{?RM@{Yq&O|Z-;7yqokl9?oAiZgW4{O?rG4DGLZFZ}Uk{?pIYz4loz zD^QGD!{R4xhTw>-A@HEk-TKi;zBz9$tQ5BLNw1Pu2!|~X3qhxv>puQYHAZ-T;vYoK zHR8;Zu}8+3pJ(Lf>s244?8=%Hwa+!x;-HbHkCA4iBi)*4J9I45EUwaO-^@)eK_4zo z?vRjs!vD9IT6_P~;r+P3$}>z3h>>cD*P4|nnIm9ttD7Wi(sNJ|qG%l42&beqlV@x) z#S^QVWRSThG5w7dH}Ba*A!&ZlWQp?&M&YZodu zV0lnr53Omvq05ho6bUM6Mp87_gBEA9n<&uX4Y)bKWU7STv+W=oBof> z_m7`z)Jba*AW4~nip!4V#f9H;z2i?psL*bzqLMYz!V^O-rAqxr>)hRU{Dm!MEeJs? zW?9^JL#@Mh+7O@+*!<*_=uurR1~BsB9}~B(qcRifJYwD{ac42 z2d)x=;L=r15iRqEjiI9#RdX8shqer?Qk|v$!x~-hy09cDFX6wrH=Ry;Sx}%}NW(KG ztN65sZUjmLPW!lbV>g(o&_)ymPDr&WQ>IuuZJ>Vps&V#xPbfvxMDs)=CU!%;JlD-Y z|HNa;tH$aNHkcmm@1J=U3?R~$>zK1(?fTOkGZqb%$cwJRnmC&I;#F$Vv?!~RW^A@5 z;_B6_NohT+w#;!#yQ@KgGIhK+3V@nm5JWxc4w?h6aCL=5qBs*^0ID1ct=>Or;60bR zK^@n>SkZP@>6E0cGbzb)ldEHrt~URrEbyCx0 zzyp(;Hvosew8?AVs9{4z7;7qZ>Xd^Q=v33-xHw*EKSQFRdgflWz(ho@fK*~0FZ>*w zuY@f?ctL1#l_o^vER*k5(}TI#FX`~H>&AV(NmVxubgN(Q(V}f63OhYxrWYkOCtOpT zYGuUcUZ^&>{-D$*3UPPyoe5!!?;VChBcAmXh4eZ9pZchV_p*7p+AGM&cjfJQ4f8wf z(%ZJc$+&vq=(xyI1ISp9RBq+%G7ZWokQ^W8^*3d8`7!Z-`MKWN_tUbLZS&OG9u-CP zR^Qu&@n6Wr*)v?my&7@r>eX`KRD+3j2A=59Lozes`)iI27cS(Edg~#sLF*z6$8Xu6=lC7i!uZVmILE%&g!@j9pxB+?thP#c%Qa2& zZ(qM&OYmdpz z!vXeiWl$~&`0vxIXiU})>Ui{@WAcW}HQ&nSy2b38f1h@^ZrBhBihzP<9pAjcqmJWB z=5W1wRwf1Nq{FIOkC_XZe=%J{`*|)rrz(=k!-rFvSXe_G_=7Wq6jpk9Le>a>@D2vK zZjyyJtUE~`NVCVRJ_uyzLY47NUPB_X`zGVOTKIfLE9vDGfj4Y~OZFfjGm;Sxe>1bY zj_!D?C9~2_2%S?;f(Qn`xT)(@oV3UTOH<3>VRn`<2Ew-%6#CwC)Dz0U^{5V~E~}oeEA*e}o~$_T&SH)Ik+Zs5^HPfF+w z&;?eK`U9LYWLvL$v%THK+7NoA8-NY;EuprM);Sz2D8ySlCJbM9@ad%2zu(zfTVtqe zT1J^bzvkeyfMs0e0Z<;%dNZ$rf}|_OP_k(MGr4$)Xq&2Fwk-hF&CsX$9vgZ)fR zDB$1|XU;pBCQPv(+tAnA)HmzQx)~9}-ddW2{YJQa9F*B5#}r~9K~blFd&Yk~)2<5- z!heQiF9BM9v-%}NHO-iXR61}D-O+yH$@0=g8UZ1!yv@lGIEy(XXTeuljx2&@U2=8% zDj8B7gGGPeK7ej?(q7u5;emDQ41$vX8u9rdDOUTWvv*j0Mcpodn4rAGp~C5;x?A7m zgFj~D=DNTCe#FRqq7j?yV1x~n__tZCZj0j^dnU5y!TUB9l<>zT;(%>(d*1ylE$~@M zRyh63l5O6h0HB1(fnCgYO@P*?_{6sK&U^xCddr?Y)ljBTfOvI2aOgouS>@RkTxf6L zhw2V-$Uw${Sc=4)o^fNF%00-0YdXh^w1Mv|&^^naF=ZTSEXH*54C@=Vk$% zhcYtll*BLl3M0f2K_RFET$(m_vhCeDnZ0rX8?^{Iabj9(LAlZQuYQ^{u-jL|furN> z2e&S@AO5>v{c5hn_6q8!h#fWSRtfHnmV%8Df4oRA%vc!1ftxMh@`w?jHe<~dV;b>N zAy_Y~j5$7~DTR^0vX=rL9PVOUSz&QS{wVB!!L&hvdKpH`+KHusuuE7F_H+4}dKG<| zE}f0hnR~Q7tXn*#of!jv;3D&yEU$t>NfYIr0uis>wrJ?VJTkPI=U3@BNHQxLLQd>9 zt?@Oe?&vh`KG&7vLzqqBp0Wqdb7igC!@tm}p{BP%`eJ-Mixk_fZoLM-1M zPmGR0|wpgCKOeP^4Fz&=73hx zfpuhxHmkb}Y?79(TMs%ZXC2)4{xa2WQ|q91`~5?n-h$@}X}U{f9Jpmc#SS@15$$9Ffp z97VxFnl*W(SM3HED!wDNYj^l7_Dp!VlY8*&&|H=Liq4=1>(cNaFx1-jH6r|2?2;)j z@A6$KrklrjXju2|&_BuA@ZcyxDX6h6w@A&J*DonkeR5%keo1+i3-29v`$tCmZ^qp+ zD=JWzAX*85TSR&Nh7m+Fg;r9>d+Lkxx>JW* z%`O^YbAdDw)500Akupr1G`h;)*JRgu_UuCDWx&Zv@Y9Vw?z)YHZHSitQTx+H-GfbSNuwy#80`eqf z%?(l{RHQ|nBfJR0yVa;UA4ZU0@D=s?IOW$>j8J_|KC`ovSy|zg?@fpCvVbetV^Eyn zyoj{RD3AfRrdz#ZsS-v9Y+^B)mT!dfTr>raG76{TDgp17ZQEK^HZ3bWTVwHV_0J$l zh^1zx+A@HNOVHRK3l<566psaCKKry%#P}RBpn?su8uG{H(FG#lWGU|*{fR!8!a-oh zXz6$oNQq_238+SaPN=0ZKx*A+5)of^Q+IVgzd!~fXzJysPgQxDqOc;17@Jj8-1#CG zL)xgD`KR?lv2G(Wj{XFR?YHXHMVIs}efQ>3LB@cjMz@A1hS-Q5R4Ghk*$W@kW!6FUgkB^Z5OO-L9&>(Zxr z{gd=xtWB?4JHJD~hrTKnzasPhNI&JzZ5^Q{ToKC_DgqfaLc%>j{Z)B!Me*YDLbaue zv5d_Q^N2x54nOF+YQ?VTrCUK3hP@#?*(h2(h#g|ED}VtlRN)iz+)pH`HQC3gc2}l` zqp|L-_yW&*SVO^)Zv*!jTp2+%cuU4{aFMmFoHMQbn5O~ckcXM-C)JntV+^r=X6!t^NE@P>$I5f>`|n75X=??ow-L%Q3$A1M068a z4NR>jQXNA}_OW-8A^gfi*2I&DWi4Pim^{${tzkf#sN93nBEn3M8F0Vi9w)(j8{U`B z9hWx!G3AK&V4Vuy7-u+BMu&6SA=*MjDa^A=M0-Zz%k1_(K_>VTSixKmno)wyyhkT1 zh-lQ!tv_;itSPN1r<(*oeU2tM6~fS7;|HwqX6!fS-`Tu$`+HKbyVjT99+f*G`1Gax zqbO|s!IQfjJwQE{mUl(6k|)$*rRYlTzIyh7unxN;>5gHqNg|0~Rb? zSax$gtMtdA$Hw^#oyS*p-MFz{+$iPC+hfN+ez;yI&ZX@|-%OV3mKZ>SWQ){=es~(6 z3m~!BMPPWrwPc_rG&zH?qoK~iRuacI>N4|H<3ZTyMPcn062v5WDzHo7SyQ4WyJmyc ze64R?pY~bvFw`u7Bb*-;Dm7wl`!UZi4TMr$Jr-_|4R~_;d4thrt0Ss5_6$Ng3UC>j z^5U>+K5G9i{Glk~4@l@@956w9?-M4SXgjE6Q@UOQ#tJ4M=i9%MGVzaC&fgygN0~v2 z@vvG2_lc<0hi+*e7H`_*RIfYoY_sbtQk!UoF*B5g^-Bxw{>{wbuAJYsU~h=&t!z=O zM8fY2Fhn;*RkK#JX8V%0yOwj+8EAHr6GOS_n3pxpNO*qxvbwM=q%(WO4KM$M|;a^AXZF3|`?#RsgsEh?u;t%%%!J1&rfkedq7B|ekl#c|tdWN?2) z#O;w`&H#;@Ty!Y!>Ah+VB8Kg1afg(3cHl+0 z(wIPK_}-*mIgJ+=k$MutD&qxXGoR1jl00ul&DNnT^a;VU^{gLc8QyB1N(ig%_+F=k6|U- zCN{YB;SgaV<)5|4Emg!dj6C0r)|c^CQOD|cn5-#qg!E;^&yJnXXG~kZoqf!rjt*}jP>6S;kX_PDk+FCYNIPiSfq*U-e>_7@ulCDZqbP*GwqO?M1Rs*6G6#h zC5x)~Ul&_eK4^5l{D*j(v*&v?5|&Awbrw}pARcS|fG?GShGNvTh+T)?|SP`GuXtQJ;{ef5g4$0LuJ?Cn#a zlHULV@evb_bNk9C5#cBb1x_K62Tz8NPW&QXOUr5PA}b#P`%8d5IB;p6rp$VYk*IOT z6F)9aXv|5%*ksHRXjyd$9699WR^lbCZPCpgKBS?W_>(pux98zd(yDQ4N=lo`@4wQC zTHc(~yc*tx4`K@{=6fb!0HlI*boN6Sd_^q?n4d#;45ns>`W1*IzR{YyIUip1*A5pN zvVF~J)wT^7T($Ov`;PBt)N~Hl+NB+oak&jH0GOG(6=-?h9Ox4Vc?weY(r@{pty(XJ zB%s>T4}J2Ezs`{EMB5Yy=d|H%ATBl6%{L2d&s+fMw0vTj7VKV zR`F=@xbFGJO0u@$R%<6AQD4X2Rt=IfY0U$j&jf5QPy%~o)0OELKR!XEisJDl$EJkq zItTa%&ioQjvkRaE%pU3!X|BcJJ(+7{ayRnG*TB3bhLg044s^3p3&=i9%z70IklcE{hGGmC_`0sy|bGUV$1UE;S1Pq{wL_*M#WEok|?IfI5sPcs}hEt?8~kmHmF zu8{0n?68X32fT>btd)HrD^3l9_^su4lT5ce#ME#n-HMq9m$)jAtql$B#ts0VdU5b= zVtax$ljvYfEsTsd$J_T>{`Ks*rCr*#Z5y4^Ud3romD>3g4t`FZOv&>1+fxbcx2!5_2c5C~W^|YL`}CPO25EJEipK z@a?qTXXZ}Kx%mJ$?oV)|GSJIys;U|ib!<#qjf9DOwJ2!W*G?+~wuLnJwf33zB7Y@O zQ%#Qif9rxut+{@Nqwj||4A_u(pxZa?bM!70F;QiW*M|t6 zJX|-=d}TbEiPS3lQL-mn{8f5hxKT7QDL)Rld)M&-pGHH*RN8R=&&CR|`o&g0m8P$8 zX6yIOUW&-tT(`KORVSOm)H!!LWL1PQb0gtf?7%MZ{*691>A9der3pL_tI{oCP&)~R zf!A8a9VMg`hLlP4zFtd$;yWK4Re8mVLe*#s#9DCEAO}e&4zRaV)4#Du5HeS-rlzOL zO!u&(B$1$-NHj@`R6`+O9l&Qs>fZX2_JnzU)@ymzOPa<3NFutH4vIQaovQZh(z&99 z9Ch!BL+R>m&H*2`1f|Y_Df8-_|BgO~cHXncrs*=o$ZKIo(h8l%zm$5w6QB)M($+fJ zvgzG>8;zZ{gE?fW-SHFdqs9)Z*XLgE1ae7_?2~j1#IB<@IgLlkTl~E=4zVk?Ka`ZC zyDet=ga}6+_<`{@1TdATE5v*jGa+h$r0%2rpsrq)dWJ$S{sv^{zjT~~Q=DYgb2 zBca(Bbx14c8$d7c>ZEp4>7a$T$?uQog4JJZUnNE6#b4=NfZi@(+~NId5egqq4(e9AT*kokGjz!xW>^w|A87w}%J9oIN3A}1_^>#SN=ryF0Fon6AE9%u zNI*S)%-}8ol`JdG6qzwopG-%UVyJW1;r@+c?d#n7Vg%iJOxt?G*69rIE6_=7V+fwz z@_W&3uY!!?OBi3bcHPU{e| zdc_%(&edN{iHwYFPDk_3?=ysiO`fs$%sIgrjAksBXa<+hV!x(}B6!SaEf-T*Dqyv4 zcJ04TVVJrTI1b(61Z~YcpTsLqcNY7-c|PR7dnO1ds~sG*&fnj^N8+8Zp~HsV%H{vx)S2Q|=m}O$! zO&d3A^m_L2^Zk-`dC#U!>ERsmnA2tJzJf|SyM|&E!b4_5VT+5TU3@5rr8N@t1Wtkc zwzT7!;NZ)|#~R*NEWi7L?tDi+WKg@sgE5<9mn=`dYn^UU)2ZC%-ieQnTinM>S|cGx zF+x?RoL9pg%AFuj<-mQ%xepQoD^zwWmshiRD41mkNu&8#OR+Yhn@L zvZjRj^)n;P8MzT^XlL(l!|Y4$%Sq$#H%HE9j|9W5BnAguTmcTDLjB2lW=@8HDTSVN zo1`WbY)CO|8hU~3FS~-nAfpV4C&eB*9BY*n<-leAgQIpH{?NL4b-gl|hldx^*0Ki=g4J9V?hwc zFYLu3UO{>l5>b>{T84$emgtgCIljO1Qg!ip3?CJ2gT_R4zc z(Hon%5RR|U175`nbZl)CF_^)wM*R(ZK=fX!=s1$0g-bV%SLXtDP=!{Xyz7n?rstoo z0fV=mcqlJ_BZ80S->&LFO-W^m#(wikKG?Fwc+7y^vpMY0oxQ!#QH!0c)f;>AR|Czv6k*bLWdM+3TpM=#$p$yK zs}OwmrJwy+=kMTFzwvQzj8Mi65j7}T@TU0XZ@;8Sloua3CER4;H;F{GooMk>6xuQ$ z8g$Juo$z9XU&cQcyWcadGAk;OaTL!RY5zDdY@w@w}BN+JE>yMk>8L9?Dp+9xX zSe!*fzy>9O1ZhpHDxzb@r{ML{uVRyt22~ti<+<;Xl#y1~OJ`{>t4N|Vy)f+idp}4G z9{R%p^fltV;pAK<)xaJ>pGuX`s)B<+Qe;C=e1IO!&*`o`(rB;l!dI@Z77yE|DM}B^ zfn}@SL_b9xs?|+E#)H|PySCg)90~Sv(93Z6@YPs)Nt6#mvo#Dtq>2Xf+DE7hqKTsEYz2V*R*hB>SkeP-CFzncnpE>Vu3{EAuEAML;AxNO zA)M!B=m^W^bw3$x_#o~!NtT5Et5?DH#Bb+8Kl^~L`*%LF@DW*BKC7Kbc$sINz())s zEfHV}x+c!S_$d9+ZWE&oIuZ?BHTzAm&MVUPW z6Xfb>3QTg3fXAx4GkLnvjlhS~=#hNw+BIK(krqw>Y6uJVTBsUiM;ui9MB54`d zF7hj-iva=k6#>sHPsS8OxW=$-JO@ncHant0bs7&4?uv#@cvUfH57F_3TgV4B4GDiv;FKfm5>TspK-G*mMinFr};?RH4&#=oF1s$p;Fe)Bt*td zo%E(nn`+N{+*MrY+l`Js@~LV5^8B15@hAC?RuzsuNZ6zwA8JTY8u^vj&jx<8|CqMe z`j+G345XaiYSTT2yvCMy0OI0xXmZB1^bT=}Rz7VGiN__tiZ~!ien<-obc#pq0?SPA z)5AF+!SxUrv>#PKj~P@}xGr^4jD3yjYUWgb)pzpL*w`gzYYNw$v?xUa_(w@P@j5nu zlqB!wb}D(q|2Ho-=2qXgju-6(N3Nm0I|HYxYhu?bNi6Z`y0fhm>C;qxw`j5p+GuFJ zw(tE`Q%!MXZ;%#q)5H7uQUdg9wcr7MPhzTE{#fI#md*VPwY9Z{qTUK2bDdRq4TP;4 z{KuJ$xJ!idFQ|dXpPR?gJWoyit-024n20k-_nRjqi?SKOP{l@*D<`asleb{Xh|XQx zn?iGQtxEt+t*@Aq}q=zQoE~rwvG`XXJQTXy_b+ z=R9buOjNl^ls0UBmkW#~QQ~{)+8v!xAEh%DtQd=3lTMuGnw#e%MU`&fK!L4}_h_#5 zA&;EVhiOSQ{F)>z%v>{eDp7nV310}*&6ly7;^*w)dXy3YuhlMhs-KH6> ztF6I`r%|~LXQCRz(pvQnu%2MMfz$3i5$F1>5d?fFFjCHk&%FPE#Xy+0gl7js_hRoU z9w$6_HP&dC77bC#IXXFM+=Tbi{!ln*b~@3nsBpz!4R0XOg4Czyx={OX{wRowkyOEo zqx|QSTON6<{p87$VOLD8TRNnU?y}>+lq8^ON4?JHS)X0g*lgF&W(GhVHYnR~4qzqB zaIaQXDo9OG+)>B5j_WdaCI&iWHfE@sq3tP~>vc*8vljI|k)9tPDIKK|ESurB&2W$i z51}~3Oi_u4UfI4&bPCi`aOh_Ps-z!paSmxAIm+11%|;OMh=%}%BO6!aYNX=SJ8?$pq%NNwv0Lvt=l*X3w&1IOwxaZLWoK0^ooXb}`|VD0OtbvcI9#r8 zPS@3SM%^~s%?sriRbICneW;o+aDu;6aa&fM@O?n#*9*_J8W0e}X8FO#>l1K_nHE@`$~<#0k0#g`5J;L!SGvi-CqMawtG=VCx>Le)tiJofZ_b znANp69ijNdgo$HGv)KrRcY4gHz(b&x>(it~n`Vw_?1Jz^XR13jD9Q>}q0@!Myz(C_ z`#=9owKxIH2Y}LQ;$q(j1Ue5P~9|hbB|;j&ih(n_rL~IoWc4mq?|R6B~mOK zL^&u0YKGc^*Ei~kfhV}jkm1AYKEZOZfBN!>jgYpX!08xX4kxD)cCA-W8+EK%YABSY zuad-X-m;|(>fUYbCU!PV`N$Y5v4CtF<&CzMsb#^M;ZfkVn;M@F-^SX`8-ZJP*V|7& z7lMLo{Mk`KZFP-$K9$xXl?~QnVuZ0to7&#GO^U^t$(&PjC4SaJmO7JXOXAx3j&`0Gjc(Qd5Y z!p4xA9L-jcunjvMOyf27Fd+{>O?tshM{ZhC^pObT$QIn@!Cw%2$bT`HG@CYUb8=7N zr@^?MU}6%HyU1`rW<1AYQ(P%|!8U~5TSTk!v*yfM&n_S<1U28-yoi0z5FE3;YU;SR z2Cx-yVTcd(EGmNw2)&d`fZ_slznc9OdgNtsyr4`^fBKq=2s=dr7xqo+Xz(UXypIFG zO6{8lJnLEiBx*D9_C>K^{WT+u4gg$57iw^?8;@GPl^jvjvWhbZVpEnY9K|vob-}f$ z7V#?xF0AiJa?wunpZ1LT(!O0gsVCXKy>F0`@(B1)wtDaqW#X0*cBu;zMU7o%zQ`9g#qjq4<)?iaY4zNlA&MLq(<=2?P?)H@?U@6Do}&RH=;~ za~)7_;_>4I#%9#D1NU{8eb&{>hVRKp-#<3VUw^i3wZXLh z9!3Qg6yIr{0*UVugk;#7_i|4pKK@&!xiYGQn>s}x7Lk{X z|3UO0olHp1Tr1K2FRSU)rgdv+JHX<-Ecvt``Ho*YiHaQPSD6?W2@3L@a7OG(!4Z>| z?qT_ddYw$j|AjT?nqH<>&fZCrbvr^4?+rs;a>Uy`zdu>9o7BzsPc>xsqVOt=w0y=#p>_RF`b)n}@p!6BVO9X2B7hI=;y*>M zH!|1NQhALgMaLp_cVdIJ5l1vjAD*TfhxbQ{vzwxfw;G-C)ihGuCs#t3Y<_>k*Rd04$S{iJ`q7+C^x0+L(deu>f7+?B@*k9xsG{3h> zHbOJ)_^bVruKs#rmd%ohU(7?tT79CZZi;SgKSJ@p6zmrs;qr$Z)`9UQpRiT0VY-9z z<46&0s^5y$_(g&e7aauL0Q>_byxdoBwh^@<&{ybha0q2BpYF2!9Ak zVd*E8nf9NvO@zLyw!?-xM6-~80G8#QOVxskkE7JTH(an3@Ijak$s1knM+4CRTK8Zs zC;TmPD++j&{~}C7KKMKAEM@^#i4tm4g#Xm%fZ>jr86VkVkjIz)9x6&{xbx%gAgbU$Q%n5 z4+O)F75wF4@8wru_hzxM2fh8!S@{x8=-uOcA> z&L>eEo(;BSs%I_`?UV@6nl%f`yzp`4z`=uuy-~wBiLF1~VS@JosO4^HC<{u2nyUjY z{g`ZVhuuq!J?fBwW>pdz+kGmQ6#qC%b-&yzgi-9k=F5xt`?ZaI9lm^MA+!wvI8v4o zPjNu~TW_UdXI1E^hsGB>ySWkqKM= zO(0m?I+!*l8vipeFfbx>c?2$kQ>`XK$dl{FWH>3h$NzmYXx1cuZglRzrlEB8LHebo z9z+k)M*^-%O^FcxdsH%Yv1E2|j~P>)K800khPhlaRGYRR%f(28Kp z1?A)j4+Lz)cK%@_O{qL}o^b=By-xHy0tn2S)&d#q2eY%p+mC^j#f1t#l|AgovME0^ z2giMBFzO#8;qO2Fa)-3uDviF$HV2{9na~WUFnsMU!K#oChb$_!z=C*^Qs_OnJylf1 zC<`WmCW*dSv7b(VbgB*Lcy_$Sun{A!k+*K42N>seKd|8!3@y!YdT7wVv&VbNf~JDb zG9uO>byB1SubAPmN0w0-2f!MG-HpYJlJe_ws-~im0}548Cn=AEZ^C)6`#9MHtdiP{ zG)CkG9(XIk292vQ`rAzjYY3gb9m1wdUfLFPTrTKWb2D^O`pONR<*IUh*?cK>NDm6= z-_1R5YNU_GYy>DTtKktkimE9x7KFQR5>48A&TIMTjLszV4BCF9M%BK1_ip5(V`NTf zjs^u4uwa3rPWbD|!-qbgJtj}Y6oeB~?|4nt9zlpsuiLos8Vsfl(B5JeesY2uMtNeA z@4l&jUjbY;E>@!$(dAR0-+tcfS92^)&RATJW}5vHF`#YNW2JgdvUv$Eb1g90&?&gc zv*V@8W$^6SaR-a&9$mXC;>1P2!wKK%`@8NeJ;5_3MR!2$Xecsb`emi$gPWvRt2XB? zP=@PoQxHFqmqxN4OG}QJ#e9>2`ufiC-=*?M1{Jd7n1$P}^w$e@XtK=|ouIiXnhp_C zwo%TvT(?Vlor11k#*>c}z8}L@ncn9Rna2&gsRXT(yP>2Q=)0lG^(tM--TN3mx$$}- zRT)3lBE?y%fxK08wCOzd1e%TCh;0y%w?+hQ0ln4`Kitx2tzmd|v*s88TaY4ZQ@Fp=tOf zrODE=a^2OGCElD*f}p>!di}Ec zvfUxyh@u3G2vZRcPy$&do zb0qpQ#*t1(eqG(KP68O(@+(WgMQ{d<6hr@^E$mw_V~It(zGvH)h&z$jnl5x;aI z^;Yypjq-?}KoMm<`>{AZ;#g^(N8%JZzkqvQcDGy8VQkxa@J5*D1`rsJ64~?AlZ}%Twu5^x=+4y0NFXeQjzX zYv}VQO z4)r05P65m)Q?2OXqulJxn%W`U^1KAKFtRdcL zgmO^2Keh7KhIp{#&pXA}t$JWW6STuG`(3=cAi(l?=>R&aw3$nsizX4OQ_VkrnK7y1 zS=rfFS;dO58gwv%@sYLpEjdJ$)t{~-f^iF2i5;x@UV5YfsjUY5OMhBKuS}_b?Y=FO zn`UV1^Efr;FC*7+_3Pg3dUb_z0*bo0MxFJ+`WyOk3@o1QV;{orz|H)fK zagiZVb%31cXcuJH?v{@~YLeRp4e)QY|2_hFSya@-sTN~axcjpUR0iM&7w(U%rOZ0De^3~-`SAP? zgQwOEAFM)-+;-xkX&f5ccKhvlu-TMO7E#r--@SO@Tkrf|5U^P_Gzu8eoud<8f~1D- zhiGlk zHm~eqkrv*7B9e6zn|LZEe&o5>|NOxj3Eejdfoi+hyk8uXzLl1k|1uZ&L+^J1*kLB% z8R32S9X#34eol1Ki9I(Dmy5`dnO{cT!s0^urdF+5PLWPXgry2TTB}%$hp|^k6K9sM z*-g{QitkpLRkl|2y$}>T;NE2w$cr`3O|GGcbErZOrJ-}5Py+SZy?fYaX(~KGr`wvf zYwLedi5Ycrx!q&*9e%`R>0v|6{uEHw47uziu z<@7a-jnB2!OG^ujt?8c`{@Yg%UDTvp^XhfzMkGBBV;{G1m|I7oApwQ2&>c6Ych(Nn z@@H<1idkfhfwK8Bwj%S#R8$wXGo`S%2Ek>6)UTzD{Df8e`6t_}C z^JDTvrW2~s&FxN|Ia5g_S8y(xV~rSaDm3(gZWK0k!Z6hCOPc`gaIOFj#(vFfR#wDJ z)*}zXg*v81Co}%$?me`t&{^Uz@YI--MD(do`_h?s!y{|0HgBK*u29(h(IpM|rNyJ$ z@$vOphNSVI-P&dk&d^CnePU3fyS57#-hE8J%o%vv#Os^}EVryMjhj@pjOMRFO=2NoIm2L|+F6SgdVe!%95{!QSYwnQ$-wa=zMs>Yvk6cpL zY07ji40q`@#z5AGnkh|Gh$4k3!~))+K!~|OQo5VwA?7*6E~k+$7I@IbD=Brg#tm=x zgrcH!+$e6ASk#07;p3}^ctWhE*~^6Pl=j&2jQ^UoX(1(LshF80Q0Zr3~{R}k+j#l^)*;YwKb=SGB5b5fZ(MzgL-IuT|k z>H;bT7&=KMVL~C7pzJ2o?1qojR_%eE(tmg_j4au1Wl53RJoO@)GF8KD`Z8&d^k8b( z`0aOHA6A{SQ+K_P_LJJ-oR+6y_I21}gnTC(JuI0a0ZSGws?%azDA(u(6$a6F<9ayG?ly!CJ|XBMYqDMZ3$mUc!G86-JokMP~|OPX=F zBk-H^KUbZff2TSLSBkgLxG5znIF4^-ui%DT%;c2wKj;zWR2c9iAM`6ng6m%CM|NoN zbktV-DWJlaC8?Q9S0boT>dkvnYGKKmM(p9Sxs4)-7^{d$G-c?zthL@)V(j&riV?`- z_fIPmJB#}xsa${+k~#`Y|jH6~I({^6k`h-I)aia~5C)BbG(!bM?mvE^b33K}oXS-c> z16QH2W&Jc7Z;|2F3>O&qe(-m(zXU*vkyXy+0!uPuuoB_zrePpikPMv*Cnj;=BA6s{l`;cor7qqRh zJ-*DQ?sK2y=+`<>#m~8H4P*0-J9hJ(?2c@&s>&p!8`O}iv?SH zpUZo*dq3vvkPl5#o;GvHP|1C^Wa>P#*PaodLyup}Tv1qXmZu~i+C6_xr&+jxyjph9GATPbb0Xu)dY?MhtzSP1(TckI%Oi*Pt*5XwU_d0n zWn%#8je%zpb!nOx*RF!6)oxw8IlVtWw8+Z#F6_-{IX=jA(KEXQX)~Jsm+6Ab6}GTq z5(TV{eS>{~Yj81d(dy1JIDK_mL%I|=yM4>qwiMgh(@aZy{oG%DRO zKkaZ9BFAW0W6fAP0<*0WH=jvdrnL`-y>wwvUHbgb>UyWh>!SBLyr0(d( zi=Px<)1i-UeEgE--SKFb9dhwu-P-byqFib{8rB1HoKTAyo+p+Y_Rbp6JMYz2InUQxxTw}n%8x(O-Qcpiol|JW zLSm0B24J7GA?Z2D=%N_Wrjtz0wW!f2C-w_1Y`k;YM~*h^Q*YKDn?VB{jolryZ}mvH z@c!MIB=;_NdUrwb9Dez@4aCIgO)I`8AF!^c_{6pTYo}_RnMtE(jeMlR@x&&6NidcJ zb8g*G+sA4{!%jWZu#PLb?H<^6`_V2P5C!Yj4p}-_)nkAJy2^?PkxY!*^L0w<-rwc3SurzZ&!H5dZiHCfQ$W*H22cg4?T{nDJc z!7bIr>mf_6FiS0c@-#OuFqzY20dZUF%^mNllRoNL#`-*D<*|UU2Zh-6 z;q1eEJt77cpPXGoiq2p0AN-qs@KtTg>PWICI()s|E3;M8S*zxTe~PzxJp1r9UWZi` zfQh|enTSI++lEwhnrPcdC2N)1*XFv5GamY<^U_8vS7z^)0>W*Ks9$^*;{W*dW0ZMU z#^3BfD7n*i&qn-%x7)7%be{Ef&ZbSAlGd5~!2;#x^KN!t;bR$>f8RC|Rc1a;X72P% zBk&@gv;b38Q9-5uL3(@L-BSEq%Rjc6V!rVAEw;5|a+4(gJ{`2Q+>l7?qz#!D-@j&` zF%FJg?N&`I6+P3r{-A$3)c8r(O`}y84Sy#seGGL+?arY&Lv*>Cy^VS$Vm9O9UNm78 z{i^$mul=-^+d3Q_uKV-Dr^+(=pc$I_HXQNYsfGCo#j64f9^DD1$`PAv)9yd`L8F?P z-CCs$kt5k{y7+FHqC+y-&rGy=Id4%*b@Rv|)8xyGDz<4_Jlq)Yu06jzUu8jCVB;xM znpJ9QYjoG<@qzJE>=|jK5eCs^6w58|bw7^RHv8O1J1lqF3{T!v|H6UdL-nb=ypM&! zJDap)+qIWR-DRG)Nt;mgsT{#@?1K*6O9NTnKv-_8>Ri!|auA_F{o~=EO)^eDna@Dp zipFP*Ll}#*oI;<0jT5bgJheOE0q~{xe7iAJx9HvUt;XcfnQ^ydA5gmQw$*J)Cc0(h z;g(>?3~5f{iIG$b*LUdQYrQ;s_ZHeB%S2?^YV-5bX!c4gIkw^-A#<%fCmp(%cA#aM z&C<{E+OUq{+ia^9KWK6y-~bJExP=UANmwwew^iIxo>~0pkAFjcyV2nvcNINk+5P|j z{ktBm*4es$?hO08+VY;Y|G6~WxOFSA%gDp`3iP z66agR>zkrq=T(i=akCuFrX>CH724~whHFlmIHlLVP6hjlADX+k)q3VX##~ zVW0O+T2{0j@pJi}xfNLx?ktKDcw_v{j&cGXugdswy2jPq*QICLZ}Kp=-2*A<)vZ_W zfjc4L){?Q#NuBoV37T-6g>lH?dqCdu-o<0r>N!vE>iCwY7+@>g`s?dUumHiMl z)UVok=ELg*b>WqG+2hrQGfhoreYHt@U>rVo=G|b$=b4FJbkpEhO_E&4E_u4ewyt8L zSu`aTWyA+|+c}$tO%2?T)n!NPA5(nVR>a=xsrC(m9msS%kbG(8c$`3}3v9vVfp<`R z>NV_QJLa!O-x9pKxY*tj$KzxbQ`Df<3vo`YH;|TF@U^7Z?rlGT3(*%Ir6Yo;pqG-e7p8nIVVF|)(c#|M{Btv|J&L)MI#*W;syPAxjYmYc>svl;9-dE5@>=09Jf zG#QenRrLCIacv5}&SH(Q)F?W**a6)PiWlXQw=eo((!A(1yBfQc4A;^Rw%>eG)}4R3 z?b`T1-|sxLtJ&>W4RzB_NNBYF$>+uPBRWO~ zye+WZCbhymzXQo~@Bcj)i(XrnS6Sbe8LvNS$6i~xPjQZaTeQ-bjL~VQG&MC{sZKt& z0Xh4z$+2t<|8>7g4~4bCJY;Fz4;ub+Z0c7%^lM}?$l&0pqN)005`-+6So(ZFtVMjs zyKHs;@dS>pTZmNU?xxxP+$4C|vQ^0dYvOBb{P*_3BhK}wFFoF3<1SfeEN}k2G--Y}9%fxhc^UcS zP1L$Gw@2vK;PH}M?8*&|x}Qw6@JZ8s?`Tx?F%OkTC12Tcvk?t+%|&*-789U1E_-=YOY-ENktle=K{kClB6B z(@hR_D_V->-6DpTx~#D;=`?hpC9A2GmFLf(Ccy1~9@O%V%KthEx=+yEJF+Nx39+1^ zb+gn4wxwif7QG$+n4FflK)#XX*pi66j*5!K+Kn3sW;BWxuxY3M4gt3H@_C|5`-dMl z{em&4PllaN?X3P*Rjy-5gMYViohvLgUX&B3WkwE`w|E$Ea%=JBjWy}B^!9U2(=m-& zR@5vy!#w<04o=o{c`mo-!%E)0`%1s!MWXraZYpst(rW2nFoFP)W~-~NUZqNvf7!bp zpJmnwHh?6bN>tIDDUSFQrEAN9o|oQu3_a-h?MG}At64marrm4HYg}E4bRgOeEj<#8 z4=6UODAlsO%Antiz{utmPCZSl7C-p+zm`{vMt4XT4zMUt`g=N-JSoKZ*RA-+Yk$Tt z-TN^wWc)DUEtY5W%}c! z*^+kSlsvR37zvqRvDSTk!(Fzr!8pGv+1xEFo~j?XzGOb}k!FO7q0wx3!K`hEtSc71 zBlpk#Wx-+}oH6Y1z2Y@Np5prR9ZFtM{6oLXCWlFDh#qZel6ob|H^vT}E$>_T4Ba5TqQlFh<^onI=BV8Jh% zInLt(B`7T#u-%XSym&=nA7N3HYZ=q6?j_mFiI{p^F4lQ6^W{gBIwgm0=~{e}oHzQE zq-u&~MT8k00gsYD2{2!$UcGuGmXw*jy3QU&^v2C3jBKoU0V;W+$>tZ~YrD>ob;hUt=*5*m zkrv(QS+SNmd_PP5E|l;my0_wOZyfvj8}X4~56NA7O1`w>QSM2PIR{ZXavDkwFwy zt?BlnRLD7!Sp~+ms;1s`uXIG2CEp`}z;A|oc7CfLOU|^dF~#VdLm1QS?=++R7%c2< zq1U2~OkE0CwKbxz<(8SZ_cW@cx{sGMRe088%QTyUFGAF*R|Q_ zisMLYC-2w*uABt`U>sFVj8d;wG%bL?9tEt_OhrX-J7{dKu03CG8(llR&9A{}$!B+U zsowTNKv0moLuPo7`C(a6q~de#cVlEQOdoHfR1%HnD({BV)B363-i~jQBQRccmp9;d z9!np+#-2+<0gh+qe&TlY$*_IP{jjuX>)*doy z>O~{v19lJjvBej?d+ZIs8m*u6M!#xk;>nv>%|yqv@m3u>cI=4dTbfvhX-hkS1DW-@ zo5*FJ%>84=kGH-b;qd<4EWg~bqeshhv3mo)y!Dq;W5UK+93{wy`P7>@>(Og^aW!f= z*nRGYuV}N9S61!d3jC%OboS-?iV5&cnge(R%SSza;i5Jr^D8)T_c=*@K0j<3SCCH+ z*e-8S(Cz)@Q+R9V_&Hbh#Aw^zH7oj`b@fLO{;2w1|A2rEYG0pMVktAu%mc1yce`iu zhU>S#Uu=(WVdFM?7W4~Gx5gU1=f~?U(*s=MXQIzJvSfMx8y))6nRLTBnx?l}HuaO! zp3Jk!?(1gC`e>Q9u#&AC^Rje|tc2(bGMTdUuUKP)!7ImgbnskNw0K{tczsT4q=x6@ z+LTg*a-V`d)deM-~dl#?YyChLZ7yCflka5=FnOR`s*I@88<$tFP3LG5bCZ=cqyK%c72&&D% zXp{}IXW@4)5h!xQIib_+l1$vE+tLqvE*leZ462s8OOjc+Igbc}6-2Z%Ct6muB0!A0 z^{Z|-UObWGp&No-IE{PVEQE!Wd5B8JryZspOhPm%Wq3rS407Q9^K2Ap|3 z=h&b?d84Ay))YS4(5@?-?yLZr9oAJgLVYXW|9kVBFzC^1Dw}WH+x3Ma|M;qBdcY|U_o-H_ij5WlnQhs-_nCh>q@aD;J*Hi9K47ht zXrHoo!?}59o^}6;?a4_F5$!8H`(kv8JDlrx*iQ?f6<_*C`7zoDkZ11i@hz%HgbHtK z%zU2}ap=&@{Z~3LL9szr%?!&PIferld=#_k4IuV0_^zvn@%abK($h;vs5kqWi@q>6BL|@lj|rI|NikbT)mWG1ZwH(nRHAgp9FXStR4om=(E#9 zD*+KN$S1zo)n`;Vb*(h^8XHSFzpJm8Zp~Awgyo44F9!>iZ4&g1wRTO<#xargC1|4= zXbK7{A9fw#lWGI~eMJc!R5`+B8J6PRxzew3-vcp`+uCU=2;iJsZ?E^nD@E9vfkex5 z^y@i`>}8oRW@sOLSk^`^03&ZjsLr9mWm{^Id6RD|Z(~x&?hGBY*x`2S18Z6Q+gSG5 zI*la(0ReW9ESLrP_7s9PE@ zA$Xj}GJ6?!VlygU0U9B_Wx)anN+LWg9?clJn$CToL8?r;c^5?xxtnL$`&tL{~tPtv0ZI5iVAnkPnbLA z$?p&U>$|2~GG;b#TwD`2%6z{G^IAF99MaYbRC!Lj^tS{xV;83%8#f!Bf?7@9$AEHT zuLCFDjIyj`SH^6o)_khER8}ht9A#E>pPs_DQRUQ~gp%r{6<^On=d&4%AkkQq*Jwt| z*RNkC9OiyT1i%B5yklbT&({y%NPc{Q<>JJ+?AJRxASBL2i)udGPu4pcN?8Q2O-Az< zFN8n^2_01UEOTh|2IWNsN9Dp#G7uD(K-b!wP~Ink5>e2@Z&c21b)b+V6C(Kw9wiWdooNW$Y7{;-F zx>Wp4ZV7B8s{9QUm@MiZJW&Pm(a^;%-#Ldm_FL#~mH+V{IVdc7>|Gis_TY&}BUNQD zNaf;2nLE{Kd+*l6O`_s!C5rAQvD`c;pRI&Xg)sRAEmeb~Q%8I(h{I%YsOI(Nd=;eU zXF%Rc7fGm}sy6%RUNQyNIpv3+v!T3g`~C{;hpFgL?14gUd`>Tw3}g^!k#Y1Q?*!iV z)#j4O#Gh+>>x~t>piPr{os@l5N;J8z!8{XdF++{d$*O@Oi;B4F>)MTNDZN#ql{YVJ zy!Yhg;|1Wt?#j?nstPr|D(7IM`pN_~#3$PxMMp=M$;o%u++fRbgk#9WBF(Gtdi0jB zuSSjPFF4R(z%<0gJkW^~(~QvJ0(;3fD=P~_fVMZdfZ6UtRSIGSpfc@?s=X|ZH4iF# zSDU^w5s>wSEx&LLw<-^UJ!YsYuigeDhHo_^1^Bhfe3gfRhvLJ}9oY5{L57;zHeIM= z8eaqH)S(_9=c;fv#TmF|`Zo%{RaK><>mKQ>9v=RZM%(xXvWBu4D)1w|&iwl7!aPAi zblZ$AJ@VT6hr-^MXP?$ZBpfJ`I;GeD)2|sbb<~t!fE>VMTXxjg?2EoK(^s7_rLdh$ z8_KkgZGN0no9&4Sy!LubGpR#$@j%_4b=;B3-3NG3j`z+g1{X@YKI-|NB3)Z=9Q1T- z#&Z2;a29Zud13lcoqYj1tOjC9xE!uqK6=)0m6>>DkhG(d&9~aaOo@FrU|sr)+q8=JTQ_-w3uAtGBMgRXp-JY1l+I_OC>za+8x@~koG4F$m8)iW zTi*n+A(OJ|dB#L;E(-OS_*7omdr5xh7@p>JdOamYlBRRN(5jSCHJQ?*nPNA8Y!Z&E zIK7xjHqbs4w*c+g{4I2G^LtzGD0pMr%c7<_xx6dKwqi$0_x2OF&LA+sdZ@jCB&Xt( zLa)II_mkXv&{-lTbCW`!Rbo@xgol_Zf_np6iXjwNZM!=%xXs_s9;nkBIE@_i$+IH# zVe|k^Gb7~GY%ST2T$ldG8ePs!C-`2VTD-k_ckYVnYv^>Q0+KwI^&J}Dh<{tVb}catXv>=?Hmh#izC9gQ zDKRH4SF{ZcAIiE;p4NuorHL)82d|xpxIp}y0%8Ff(*PyF%xLDhOkm@+nNLcOth?s; z^VOS2qFCis>$b;-{T%Q?L4*l_F!z4LMuHAT0?68mA_y=s)(I2(juN6=Xn=@&Sz^&y z;Ic0>m?%*mtC7>uv*?{=@e|v3y^J0&Wp%i+l{ad{aBy?b$^w*`-p5flQv)fCrm!Fsg_Uq(eOnGwE16H9J2>fS18Vt%UF2OaxU6df7E$F_P==y*J)byBBJ7zZ@m4UU@#hN7zDXQ(ee~J{|gjb%=(_D9$a^J{?!-Bbx#5z(Xv9j5st~ zB;p`Q6VMw(#C;2RTL0*E;ZU#=z*zAIR0oCuN zzrv5FmH5N(WgiQ>a|e2J?W@(!%+#oElT-!)9vymDj-ABwG{>4^!``0qfkW-a!2dd9{wOUOL`Z4D$Xw~F-{CpSR#c{OqImN{ir2JHWO)(-?9`4 zQqWBIXal;m^f%`zv8O}v!Y?Rj9OwWIxGd;@yyX1gn&)Zg>LzJ)^i+6>X5h3}GGDX= zR$2r=%!e>1P}*i;tbj#>o$iZ3sy;kdmO*K(uIM;ZCSrk* zJi;~?!Iv;}7PDz*f^)X_#`U_}QFoIWcajwg!Mc8*fR1O$+|eBf*LF5K)WH?_n#F#& zZPSbBCDIZ-*9IwDWH69=*?qs|R{>LVn!LNri%Xh{y3$L`fem_=y+^%hN z1xALPamI0YkG9KS%nlUkSZ+WpZr)^#KH*+Cqi4F9o52M_hFVE`Sf66McN;yTjBw+Da{R({a0>lvyJfwI!}jnI&x)D~uF2OkU=Gj;g0? zZv|ZJi+;E9wSUin4^@6djH8z*@B3lfHbXYT4AbbFADBBW1`#IFU&?&4h7)HHOqZi(->VR6KiU=ah5{c>ox`h>+^@rc z)FsJFNj-TLsaxJ-aMdh!|KUE{l%7wW2;XKvG;|u-r%6%|h8f8DAm)*7$rd-na$l`s zkt{>V9=p1Zk|3g-pi{J%NOEVzOtHOdG6F!Pd+%VTlpbN8)TVhuZvZ?04EBb7u322w zW$O$$ytWA0rqofD0j3e2=%F9c5<`xU@?8Y5`>tuD2`(W^d5VVaHRjX-9_&s2(BVqdPj`one3-?M4c=qseE%+wyZD3 z@n<>|1Z&(7@V$I`8?C+Bc$0XTU%ab zWu0aQImD1V)GGgv1)~3rcDBwE!CZyO|LS*A5S#GWso6`to<(?R|K7a(KyQMJp*NxEBsaL~Siw4k3wPo2J_reL_6m{Hf9T zi4D4Sm!DaV5L35NeuGmifmw*BU0(T@pZ%E);Iq!6Ih`}Jyuba))1O(3i?r=mfH6%_ zn<1o28?q7~`bbxJ*jM-941$yE1GNh?CPbF7>EOfk8C-R^jhB`^X)Dx@Z0Wjh2HQd$ zQ0s3xu%4)!slmjYFh$GM?Zy~EI$&QU2Rb4}hRbWaFsnpnHv08r1Ll-dT=C~a>-?{J z;u__`l4+oLLw?-s%eMRQ2P4090$Rx?0H00S_1v++RPVL_Vqsx#E19I#Ju5+0wMZQ? zj&ddOoKIHoHUN7`diDW~sOjiOb%x>$!kerk0#QEbt>8@YK>p5V?@eGT@l&lMR`hpP zR>A~@ix(j_K4VP@OM?X<@VZZRPg43C#&S%7CQ21|sQ0H|O13mTOB%aFsxuB+f)xZ| zGLI{g>js&1qC0TlDU+si%H1o&T?fAzZrkrr-m^QY@ncLuM_oLV3hRt)89zsE>jNo< zQFhV$_b=x8uAE9NHy}Fq#b48_$W%gyQJ{TT8?KBK@@D(;c?AUpgcL~bj3PfQSqtsL zUyo>xY2ViSgj|^9#Rm?A|5D>4!!<&CZN5`%+)TRybg;-FA3$Tp&)1^92-R06_k3#1 zP!|lG9v2r!cMVaH3JpXn0~ge;(pzc|nu#i%lBff`96(9qSHQx(Zo7#Or276#7EKXa*if{gg74YQF8NGlNW`JdD%5yk%ghk%! zaVi*F!0i}FTu|svCL=fYDDV9y)r^ckuFM)PH6I%b?R(eXa2}+tg6_=?Hm~{NHw0iv zl^#~ga0DtN_6-8B?GB~Dd+1J8;<6t+{)Thz1s^I6Z38EQf1?wT+8P>Dh^$% zekJcs7Y7@F>39r?Au>MzN4?oQtxdFVGz{+7pKyfyrrnggqKQJi`d8e(yppe*K^A)KeAE09A z6Jt(ouzss9m>%ie7H*&02j;vdB_3_pWpS71p(Q<{PEK4S;;tzO;ykEMfrzN+iX}*p zWZ#NWfD+F^i#i!{nI*70dOU!*UI}%C=O!L)sUXHL%0%|L1K@KrK}&GtKa+*b2tYhT zC8rzEd@O_TMH&!i?WZ~dl93%sb*?WGk)=RzHo=`9%(sSx*xndJW&;%Axqwp-!Zk8; zQrr#>N`_Wq7qFFm^n=T(aGSRAr9Y+$ZzSe+OeF#Cjc`20@XLigI(S!J%BK<5MU=Im zm!*vk2-|g^?!;vW%1RL|vo~AMxvQ!f$f;dYQBg;4OG``Tx2xudngS4`h(`cU_Rb2# z?ZV#RYC*54)7a&)HobUC1~mAEaYZP%+yK6sk9exH=7w(t5^vq=ZeouE3EyR&FNosM z4#19efqIZizRhO@7sxgj#%X}Pc?KPTbo`5?R3{cYDg=W~2hPA%gls~K7Tpuyff?{1 zN2sffVwgx3_G{fde|~EDOXY+ihNod!QN;*GL*Uy|{|6q!GMBgtinn(-Hz$Q5M$AeyAIfPfJtU}%T20Lw%WSGeUN z7)A?tH5Hf$etu>lht>+9K+D(;ZCnnZ?O=^a$b>umCCvhuKyvUKhr#;HmRFPzrrzE> zZ3^%CNh281W0F}W2W(n&DXY`Jq?FHat(3|BLXj%t%C1s2GcVu$jFRwrXYEW$h0?ej z9SsYMZ7C7G<0K2Ys*Q6vI60X-mlAKft zxS!K`S^JRZm7311Z#we8$%HW;smthhnWDVh$cYLyGQTC$~k;ziE7mx=LNRAv^ z2uhA1v(07a=BZ`S?Rg0(RXj&}-i`Xf>alg1`JLq(i6V;a~gdsX2nW*cF8Gwb5(*96IzMMcJ zGy*Nifiya-?8w`0+Dd>nc;Rot0v(`F;kGuzI@V)7ns4y+nIttn0a7H910zCN-Y!|n zP>QtYTn!BD`Z7SJ3xdm@)`WTmv|h_Wm5LHMDp_!My}jJ?S$ zePJnl``SN>qQqNTQ*;IUfNtv5uOm1)IWwVs;F~f9_sqx8XT|Myd8+j6>sxpmF2@4t zKcN%z_$!bzpF!eoUj)Iw!faH$?2zi#PM0n;E_dqirp2Q0jS5mXT8PBcoYd%Kp420y z0u1X$bhK5H`_(u5VahDNxN~MuFiKCQ(wg_8t$lA%9IqrDocI$kRaEr`NRJWJ-+G5x zqM?co9&~1qSS!#+maYjtu?P6@{w55m>1(t=V3pxA(BYCvumQ+rZ1O3z(GXP<3Y}@c zPR;VjkqTIBZ?gUn6~?b{URaWNxqD1!mb!k6$=#H3s~jpSCzB5xeiYCA&=>1t=KRz$ zFwSM5np>l45`e+nZrwi1`G0xB#Wiz?aFU6a(#$ zIxs-N5E%?1QBm{QY?Ao_T78&fi6G09I3}^hd%P(83uuT*z84zG4}!>ayn?sZ0HNrZ zpi+VPh&}**UI0DZ10d@(#N^6|2MkdMI&|bRaAyf%Wf1md?~i#pyrhF)Q@`D*KYug&ROpqUdI`B3^+zhMmT!=madk7wQg5f|U)ax{ zV*XCjsgK>o!}oPoZFZ7P23P+6&cltSj9Qa+pn><}<7G%?NM=eyoP)Gu_OoGbF$Wct* zx1oyPj!rU5kYss#h@O`HvJNYD`nNpBgyZ7eyt0MQLgZRjxVG24?r;Lt z576-$OXMW#YD1+dlXDk!3+JK1`y`o~U^;kc&W9U-Mh&y6s7gi&X)-U`iFgB!5upuE z5&I34%aQ17&*x1_uY=U_nq|%ki<}kJVAIi&CEaEPs-?c0 z?sQ~=xTJ2mvJOJMojad?c-%Rp94s{Yi(xLQh@o&WZh}N`vbrc~48+V6oe(lVKRa>3 z=h25B9h64FjCAcWK5&$9hNu-?4kj)3&aagYycT&}raO6FtnPKI`2lM**xu(>vvnT# z9m6oGJHQ{R^meYR0EB}5*+e45<*gwO^&<{T;$DNaE;gjM4Sck&I2-7~MXS+T!D(q} z=*h21ijW&oBOGsxmf*W37WGB)Ys+T1rlb}`I8@fK3PPRF zrYw`pWzUA^aV=y5{i!Dr#Gkg3X{htX+w04LJjk(3;yaU#^Gd^pC2u4u_kEr$c`<{N?{i_JcqA=kxWGkeCHp5b5pz^j5{AvotRYGKqI{rVJ|s zhOYr%P1gP2S?=0P_keez_Qzjod5C(66v+=#JcpGE&rnmeW)%_5uDfbAyhL>2V<6@# z3nQYWYwVI?x!Z$+6V(%az&l+=YU*;Pe40!2g(8`eF2H*?~ z<}V7~Z)MluQyKS*N~$iSV)+N3?baXam!W6ji|f82CF$=UF~9qt{oJIPNL@^J_u$|E zQqlb5p{Q$w{`j-{hZsN_slKgHvST$Kg zd1r&&7fUE`&y(hYl(@g?^Y|0t7eo^0S)A(j?Of99V@&H3eD0U}W!5E;drUj5{l33iDr85p)42$9F|) z9_kgK@9}s5W;{iWB^F2(pi~kVR!1;Qjy*a*7@iG26WT?HW9!bOXgM-JiF(*>Npa?B zkXZE}j*R&y!>cp&_rJdUFZ?`JU^B^#2A6i&>-~BIEad+{W>j(^@Z~@^hFBZxXrU;kw~o zaBy&rG#wFIK3ALDBvnQ{Q~crp);aIsFY^|#Fpk0r*Eh_BYXn*zU{`y3i1s_i+H?(> zny7~|^LhUx>{JEj{GR%%TH+U3m)P@}` z4%vNS@PKgOeuv?2C=6#x5C0S_3cvLaxxs%@Adqa@oE5)q|BlncNnVK;?fps&vH2Z{ z*P=bg@MmCt)k=n*$GEqkVpzxI8p%}D{d!WW{R7>>kxT!g-;0*{kiat(U;Zf_9(+apX$UH=Ta7l^`p9-gRE}ME>0+iGT{UWWH~qRs zT-fFHa03Wlzv_o9=+Ysc< z^8VXVxY`&q3mJHql7~ppm|kAE*iXgJly4MwMu%ih>9J((V zGInbJ-d9sb#Y4)6+?45C8Z-wbKdTZ#>wVjYBQ?1N^G)U~Bj^D}Bzokqg!O0o z*WndgWr;!AcZItblS`R&P;<i?QYM00?~`pSPX-8E8* zy1w~S_dNVU`1=3XM*bI1bexTJ+e~wL_0o`(Q9uTTuK$d|Rwevumcd|bj$HO!wQz+d zb9YBe4d;j^-q84|qKQh(-#!T!UVIZ+BoTuHk`v4ccfjeHdWy&etLn!H)ZG5 z;oKa56r2u_!#V>tfyX{1x!T?tu->5SGS|EOH4Te<}IGH zNh#aJmYlzgsjx=P#C`+s2kN5P%&rV2b6rfyHw-?sK+7Pl^=b5zM0_;Zw}HV3+cDJV z3KkJ}F9VBz-sk;3dU+?p&3wKSKLQIGZF5_$GtZvSzBHkkEKA9k&b77n*Gu)0w%cgP6YcuhN^V70T zG%Y&A6hQ_bh4}a-i;2ZYR)t$rlKr4J)?ra{Y zwCD5lixBp}bjKzwOeUj+g~d?B6a0zR56&2zXTqQYo6*U}khxV{@a)((XLs|*EZ;z~o`CJTBn#2m_)b7NQ?Xp8d+ ztwOC` z+hGaS*wENL-0}z5!l%&=Z{d+6N6!18m<8Sj=_EiBCdwR2_JFXwCD8M&GiabI$PSf^ z3Xpufgky{O$4;C%nRaSD0DARIQw~&Iu2ZYBQc_I-++DzXN^&US96T_{g}0z?4*(hW zpLwEbx0;+Fk_v{rhIQybO~P!)2Xep)^m|IUc_Vf)L_6ws0mNJj1f7OY)prhYt z{+|nohJG7m8={FA2Sk6~ckKg~)FTl#Y@>{7cod3CMYBagV6;IM2phg(4JsKxFzsqW zPSsviEV^^=-r5gU0*}C;DX9cULv>J)!mp@tWF&sZ0o{PE>azMBfgpA;i%o#41FcGD zG*HJqQkSKRI!_@I(_Z87d3)bA|M(+&L)SP&gVe!;pbCar9#Hgc=J`fYWL>kby-Q5` z#d|)iM-@qdBb`OdQ2ilA5NQnqS^qOu>ZErLPES#MJ(L$EUu{RaiWL`auDGC|Be0*SI zf}KRnJB-UA(NPBV(L;$$>FvXh_d&ZWsl*$q>SCnFRE3@oxe#=NA7(~067_P`1ll4` zf5g{;d``7cl!4UL)jd}Q4~yvw#pDKV`HizE7^lquvDPAdE?1WJ>j>y zkM<%Imt6Q)+&iFvj;K2fy9d~NxAXJ38BP`td1j)_zxP#pjQ5#?`E!OhqYcu zPx6n)Y&mtmXQ`aa*nW4*7>t8T$J)8eyDJ{U*XL-G3(2)Idd#m~+4w2Jmd0Yx=pjs{ z57U0~!yzTMwYBvL>+9DTeU4K3Lkz8Dq{5|Xglh`t`-8?ZM_8CGK0Y4v)`hGepaXrn z&$(B75tXp4U%#FP3YqNR&#_|njvYJZ8Ei(ocU0hIX~=7$W7kwPP^Rha=-9g%ld3wl zJV*Ulxvo9J5%L}ym^gxgdEkv-d4Tu4%+7X;h&bfwOS4t+8eSOgA+i7MXbj!qY!Gco zvhV9&jtNqy@Z*)}VE^W-6o&MU(Ry;o8(aKxSmDKs(`hOsMjf5la(8-RYs%C-b!9#u zd`bi8>lSsOH$V8`ESTJ`gn2mjW?`69hWNIYHs!i*XnewtPB) z^K1wTrQ*lz?1`wirK5eo=f*~M290E*wtx3p620-V!RWG_eo}ZJCa+-CcRt?d9VN+_ zJbU&e2K5dH3!rR~YxGSh-xeGMpPCm_7OAYPc&555-Se6}x%m9cMy|u5{qQy*y41jxk^Z*3{L_n=>c4cup}S z&-|F2jN%stP@S!3pD^J$tVz~hVb7X(O&G9xX|*3svIh??59~MF?%k^fEHUK)3 zZ2k=(&741W!oPhRIxw1-mlv%6iP*9yPMsQuSkcW1oux5*HWdbH>A~Wq=s^D^ zJTiI9id>MDmmc}LX1;*H+V=DpFZ7|ppDAM{gzK8XwkOWMZ4nPI@41T?Zyrf`@IV+e z($k68L2eqq{JvhwY&6I|FmB0p%w)zO70$K|*QBT9fF+=7meF(DR#{xL3MkM+jRE&bswx5znIvT;>Lys zHgj|HkWX0MrwIv4n>Iye*S*||ZsS{WJHEb}2Irw<;wMpm1j~*|%p4^kK#trj*?~Tb zY?xrbxV00lM$iIzGFsLz3aUfMsf2F+n3u5FUZe^FX?C31iKeD?TO#L8ani!cn(uTZ zjt52dR2YYd?>{AKu}MJ2P=y^S?}`IiX*33-#AIUJ13VEI=wdIRwqU^ob!e_Q8LsHX zZvY=0FtStEZ+)P( z+(1@Xctb&b}!1Wim$4Ph%38^9b$M{j?c zH;3T^TgPJ{6c(rwJw~P+mu}g+ZyzzjLz|Q_v76@SM(U-UM5zu3MTPw z&jh35%lGeh+dFY9o@4Snj)RfrC*J<`^9q}_1ui7{ zSwhk@g;PXl6>cHSqgKsR%=$6V-m#-2u%IPk_5c<(19p5eC+8_lwg`r01eCA}C$C_V zXzqkNDe#JmwuQNZQ4|cju)tXaQv30kQdOLIR2-9`aUd)ql4N7N%*s-X%L6Y&FO$v>GwIjWI6w-u95YVN#XzYu`wF;@bd`U(?HKZukwnc4VL{ zeZ+S(6Z-ww7I5&u;*x#ERUgGAB{OhuXka?b$EYK5I??4HKXT*F&2X2OckG;u&2=f; z$i&1WHFXtD#H%4)O<|;-=qy|KU09RN{_j##8S5@Dc}DgSF%|kwuoym1OT$PDZIiw+ zC-$=zMy}P=oB_-0uFr+xpk#1+^b+kxV2m|J+T2~)j|oZm(2H6e&MCYXjFhy-xth0V z5i6iM4_{x-ZX6zrEsHXGJp)Ve8E;dZ?37Sb%q?~3Guky^!sLa~4%k(jkBG3dvn#8r zvNnL1G7poQVq;@xDmbqMc%)p3nH*ZTO}8zLTE`1-CHig_gCtHLLZqUl$j(*#370K-(F zXE;!|3VFap!;s?_Aow>!(47Xe4ITb7G`MfZkM{T~IJ7T8vu$@|f(FQ<7wo1eY;FxZ z198f!(o&s}Kuk}>{DlyN^4NK)7|6EPiH!P^16QwHF@R z^aOMKPQHI1nv!Ba(C^d>VI>U#ku)zmgU&aj$Bm=p0v|$1qEt4ezW(4fcx5D9a+o$w z6Abf>`x8HxDGc;ZgV%9;@W3o&wcpWgW4zzpQM=Q+^>-%VWFHzSmwqpApf5f9UY@r9 zhdZq))75?*9+j4kj#CIJKqQ>Qk^)SKKh#zfBQG#=kWuFN-2qgm0Fj{yq%{irkA&y@ zmmY||{kk*E5m3irD=TJ13DZrtRZ)5y#BYd5XbM+NO-oiePOugIdSpd*c6Kl( zZ6Ic29JaMRg&``ng%7-7rq*9yxl1|%;=khP{nPGgK3LRR#eQ(KE6^={*!s|AU3EDV zJilWW2YjC!CYw-fk8=^Gmm&{t+d|g!I`BA{PqQ%k$-xi&tjDwXzK&wREj$NKm>;4v zj$y7Cjiwf-Vcq?{%e~Zl{t*j514Gy5eNVw~I}EK5V9C#Z_39b&y>r*DEm?tqVq>A+ znqC3|cN4;W@9(%T*A-lRux~yiFX{vfO{E9H*UsCf5_V1cbY~EHDdh_lk917mtI$4=#wfGQ0tr`fw)8+{-T)gjH#j6w5;I0Lpm(KQcg}~#1UM< zqIMZ$jF*D;=yF%zoX|~vK0cH15l=!e_zdIKI_pOy9(_<2WiU$eV6iLx5CfvnI_FZ> z(d#RZ9tYi=wNbF^@3=AK#tj+Gdccdg-O8BRkktpPa@fLR;ZJ)sR+{rZ%DK;8T3R}+ zPo9QMsi$^AH}nOAMBv~{F>p-j5m+HeP=FH96t?Hik@S0u*yjy(FKO9eDL7?tZG{fTY(IG=iZq~2??^TZEcWHExnW( zsr_UUG`^d;l1(xD8a^#GBV#PIvgx4Nin4^{YFn;bw@$*ea3VrJccf+DlxOwI=bbub z;P>nJDI3l2F4<~zu(yAJ|KzZDs@b~d(4qdEfshr@Vol-5LF$Iw7{~GG`Cot9n*6<{ zCKw;cs~((|inv zYy21C?(S~v@AGau$BrB4Rf2?q_~>v_UiUw}eLIx__cZ~zJj49BxOU8e1#XhFWvs@d zEA$kfUAPet{d>S3=~i7sSBFu!1eVVm@+uGjj$q(0ENO{x!9a4Y%14hMYanh3(WSr+ z$>GOEz7HQi-uj`jw)Qj<^^i{yy0R}aGDb46fb(b7efuV`0{1F8PbQ`78}~N2to22h z;cEbBG*liFk><|4d!XQ+2_UH}*SB6eb7rDJf}%UCk-W?9okbX`!wnNW5`mB+ zX3;qJwe9$_dqgVYqs@0V@-l9v>=-d(L}=z12F6m2$578LM-!8ilP8az5S-V8o`27f ztlJC1#=^sSVQ8zJgF_|Sn1%M?uf~rbpNh%JWXSV-gqj3)YH!aoaz?;H>1A$lcxupTl`9F}q4b8+j&lZldko9IWm&YG&Na56sWLbpHHt zg!`B_w%W*vZUL!S@3l#ZiBbF&COS|(ZTkop_c#oT#(85PXv0-0X=`&c&cfZ{$nF7R z*oh8^!4DpIWBM>ljQOW{4jTWANc3>aw+(aAq>SQC^W-kosUm?_u6QDE@H#HS#>Tb` zd2AxGY+P+x4~6e28N3x^iOYF>v}K>2z5PA8e(1@O9dNkorLZH|gps}wCMxQmu;fxf zWqEP&%#_}r6I`Wo8+!i{`_3QznMU=Z-{91WqZm^+ky~>8bgK_BraN}-+Vur_R43HS zZkZpTfZKLb_oSQKocZ&W-$CUi^eH;rcurpNYA zeh!S8NQdLENB+mto}N>Vi?rC;*_p&I8A_$Q*PxNWJir39Adu2Nti%>(K^8f4=FIU< zN9Xzh)PJ(q@nfu+N`qF+foB;RswN5uOBs`A&05q7h=pzWf%lVn{jOa*15dl=o2{*_ zsHD($=#X9jTtWChNn-%Lb#DUs(A-{(;_Z$#=)T%6>i@V^ijb1c@J9y8&0WV}@VzeUCeoFtdr?F~8Gbz-E~ zG)$O9EyvB>eXhLx?wU;-H=a}sp~+Kcaf(nCBpVZIXkPmC81TweObMGh6S?;4S8+I? z%8fBJ2;9U(=%ASy?fiTPVFsdynDs?rICBf+Z{I)hqF@T{dptF4J1S^>5qlgT8kefP zckfN1mjy3hj>n#ZBXo<2S?=iM6oR>}^_$kLnfwN#%M*00`uX;v5f4sG`q{Zkn5T6H z60q?-Fv|;qzHeK$bZIIj8#G&V&3%keuNeqI*7FK6q7N>OUsg6DyC_iJabg06VweN- z5)tt;M7HmYeus2Jn?8S>&%mVN;qYa*u#=vbmCZ)gW)<4esmwj)ezdpqp^y;JmmLgO zSSj_4laOb2qkE~qLF2u9=g7)xj}JZfy2_*^`mRYYkUf1MXm|svp z7`Mvmvy3Lpl{bI}1hu4Vc|{li2i@6D6O?=Otj_3&WWv6wtVU>aGR&R zl^XyIdXxxmyhhm~vODs(_m*$MOuBQ&wgXmyg7#u7ri*_@i;Ux!FJGP~IO?gXj`p~Ogb6|JC|xbZOh*GfpjZ~2O$Hdz&Wa2wYP9X)D*fi0bAxB$#cL}nkb0_meaZ|0R@xUm)R;Ub6LPKyvZj2gt4 zc`cL0m$JmzEk*}x*63LPTD|pX07eypfXnlh#RUb&L71*~K%h{Q?455nXEN zpw9i4!W3>@YP;B_!N1^cPvOqLPxzfP%r^y`zPNAMi&$#GxL~&YtNyViYUedRq&N zW0<8FCc1n%2N05|=;*zNrPVL?4^VQ7M~y;mc{v{g4sldSBrt{m5wF2t{L8=N8BO&c z#0_K@E@Z*sIfb!*i1Iwptc+DgMus2=dwZE5)l>rA(~!Wq_qy3tzl0cr>^ z2xn!a0tSl0bQuE|Rn?P1y(%G_^|7MZiBoBwGY&Ojz@aT@H#1jAXgtp07{=%^V?uP| zjtpV`4387DtFy1%5Ubw|;2*t=6hC6tI#PROMEvf_7&ds?XII>j1qa@T&8>aSXT!zI z+qdz@1sIRX5_Z)&ypE`Qr6nU1+*%xgiJ?(}E^Lg?2(->eUGFWj#@^EiI(Kdg`i{&m z4Z??8JHB0g<3?4WJIZzM;w+6(vkAf0reTMeFM1d+i0OZ?0+QI#=fv&F6DdY2qA583 z^s#K0RQK(>9i}_O04Ue`_egRzxYlq3>%jtrm>C(3K}|5<|7}@#ufanc)DzdQ&xReK z(UpilE0Ln-6%?qz-)xVt)YlIV%Rr*I2MIu0-6_S8RoI!!F!vkdWk;ef$d`^~o;86O zUAh`S&xdvs9ge3V7iW~Y%>&{}{SDWwU3;FV`s>&Ei75w6O`m`IL~S8vq;Wid{v3L= z@leO9cGibRMzUk$Z$m&OvE%UJsIUjy7Bxel`z$~I;mZOD^Uqj!wrlAup@i_P9mqE`1?SInu?oM(2EQ zVbrNUIzOPP7KMbOtflMMu04)Q0Mhf%H8q&M>}A^>=O;h0jD9RwDj~b1U>=hfU|8zFk*V%%j>UuFie4tH zeO*f3l)gnD`ZNn(pfhElMr(ji<#zrqw`j}^y?J=!0v{^aPEp&gm_EE z#6~s=jQ)v$%E&qBr(P~#C`Ka7oD#e!R(e5w-P>DUQMz$QjPjkrw%t9S6^GI0H-E+h z9M=H*gIRqr=m8MGC>yO&Mb#kXOzOJ=yt{Bs-h!rY&Ny7GIG+GiGiBL(k2Y7hhXpD~ zw3#6{nS`WEQG0un)@nE&kV(cw$D}t)-b&hmTb*We@Zb`p;r!9oR#tgY21+PxamMEW znd6N47`=ZKFgcBxmry~$ptz9;>IfbP4e<1M4>_Ip*|U>jrKpu;x=$oZ%ng*?5o-C3T?$AE|HzRe-E6e90-9#q0`}sOv077XeDI*o*TfA05(_2f zKfGVXeo1s@M!yI-DfpEakmH9H8h;1IMBq2-QHq^)37Z2l6&M;GCKeq#27}np+;?cw zeyV0g2~srvYB+}JV+=GgT0HOJz1`EcA%T8|S`^K)hPr-Dw5W*4By3Mim_6@Uc4HI! z^C6H+el7~tO&uM6y;EoXm0bM!Kj#FNA1t1XlnG92NxlWPa&ercAViegew05k4-&e# zCS>!P<43SrSuqot0nF6!Ts0wKDJ}{)XAB0%qHPqix>b&#@v_J<(xJhI%^7!TxtfZ~ zGt7#8l$)E&PII|ieR*Xyi?7St9tvq=;}TOY_VM<1dYz#0v^NHk1`T?|z+!5E=hhG0 z*0Yeub#fX@B1`K?$`eC2VR6t22+aA0j}!XnF5g!})yushF)s{2DvJ{f3yZX4=iw`Q z4*LnvLt_2GVMP(#p^6zVZ=l!}DHqrM*Wt)MBN9Ac{ovTK5t3#l^H~?Z*;?YTy{Y;0 zj~}~3D_azP;A2tEnc<4iDy#akV#s90N)M0sF%BJvFhtfc&5Z@@pcJ96QA@!Km@&p8 z1<)1aFvupaih*{jV>EJsTLaR04>m?&q3MlM38eg3mWBx=AH$JF`A*QqQc??U#5h?^^T5?d9dZLdV$VK+cnKO zfMD{Q>CLrel~e7cI&KBSU+RM&g2vfSeS4nsA_W6Vsd0O?mr+~iCUx~GNdomHF%w`G z77e)ULkKyZg=I>A`Mg}5CHDqoSaSe3!>@6S`8LvA{)DJrG7p@iQrwX$w|>NO6FyRO z8R&qE0YbjbK^Fk#s7;$T5z>V^D@Vh+HEU9ls?(Tf)+KoU@Q(iP#=7j-QwNp+kuN7u z|Kdb&2b(Y*Q~_0dE{eXeD7|;!KT%Rd*<l!;5BI0WA)17sDT#H+px_cBVDCJF2e^fK7aW#*G*qbTN@k!25txyqqBPA z_r99?D(O9Hd?iTqG#1}PSla&#)euz1w|z6cwQ$S{Rb zHO}YAVi6yy!tgH5fN4Q!%uxasFZdB-ap~^Ky^#$dU#;G}`Rq-}TU!>gK9iJ?c!7jM z1MSs98iAdM8Uk(i1O)EU{qvNrs+wBLg`SH8JZKOxrJ)Cy7Hmqc(3m0y<4}3BZcXpK zh}X!K0kBQ|k-J0BX+izEjTnc&sYEgt+H`=iNWoU<(pswIB1&FzK`X1aIY?p7V;qpY zckh(I|D~8y2SgKaQ$0LApMfFt1;8RJYp!Kqw{<=b()kzItrkDOs?+@eQj-bKe7Lnn z106qgrq9PATaIjug!K7PXb-D- z9Z_8eoFYb)jh3%wzmhXyx`Sua6oWq5;vLI#?ZK3QNVsl>qj_Crz0) zZC*WMuuPmgjRYIBm~V@vJAlQ`cS(@|xdo161OuVySeU&_nH>!ucZ|OY&#i*UFzegc zK9}dG*g20&7-##BKsrN_4sGq!G_-*9lux1QF=p8i>B`YY6<9tvC%|u{``y5*Gk{M-C|cp z-EsWo9AZl{XY}%1?y$GS837}ERqLz^)i?>Oz=&~g$LQ;a2G7TW7zL_Y1@f#*JU39b zMs|#%-G-WvkB!+;Xxx0SXBFSC|e6~;Y)S(I3=a$Q+bW`^<4qnv99(T13K2G){;A$h32pJLlof- zoR^VGS#AsHj`ig!d`LGKWr_V}A0DYl&t?Fp%ZoMBM#)6gXiXr(I&jLijb-WX>QX=6 z&(KHYpVkhGJmG;kD1nrh)N7Y{r&UG1L7t6@y*mh0LN!MKL!ZwT1+JB5gF}uDA0Mw6 zv|ivjIy*8LxFXJOD#`BhMeaZ0Bg8`t*MAE-8yosGwtMVxM$|{u4?ta%X5sXuqVk2U z54!2(3>8(?%kh^V+4pkRTLOW%5JnrkTTw|)JvOCJEtDDzTNE@$j1Q_KtAWsQwZH{+ zpE#R_fUrIQHX|7q*DgT+7giMHu<(;TJw2ZyvP3%-Rc#|{}o zXJ@B}VyTBm*(Df2umdRqz}b^T8zNRjV~9e&D1d!2lZ(p$gX~e#RNJ}JU#NGf!j<)7 z@q2=oQ0+V42Xl_#eks~NFKjJ{)Z>FYL=qjiHv%PAT(sg;EuvoG-NujbKYDN5-Q&2t zP-GE`LH}#P+3LI7Scyx5*5JBFt^sFom1VpGC*Us;*kaK-rGY z)};<>@RZIwTv0galy!080r%zo2rr~T{ebE@%khE4Eo?w^suV*0x$uLrfx)rQpY=7Q zO|Apt@{l)9G<$0AjF5W(Nh~uWYpAR|0gR5Y^_MMF*&zZG67Bki26yLMTRc&*sRV;j zWp>b|OYWd`D6LyJ0c@1zu2KX25!kP3{@A|ruv|Z9wLoRyQ8K1O5cmej)CjZ4Z^5Lw zqEG4-?M<50_3g0DlRDc(bFAh(3=KJc^LRJIHDsv^@MZ~wV)lOakP2) zYS%qwu-OnlTO|zlVoX z6u9s#E2}o}l!`H+c5UsRz(8XpF9Zd(am|{w2Mkb}KowG*gdhb+XO7=%_vFFIB6)k% z9)PfmH1CfGR>w#Mo}zDTEI3OXkv0IIP^eVHv|<*agP+IZ9TA_wI{>=RAwMuOwXLp5 zA%0FHGD@N8tQXOF2+-bk(?AZ^F(SJYJy7fk0qOZxL8F`GA1g@)z6O;*GBa(MW@X4L4u9brJ!q8rq(D_B!~muJ z01@}W%&tbk%fK&SIg)sgM@BI!Dl0V}@7sVVa@OUmSH0lp#|Akwz??QRHm2(gNS@9g zDt3<3W})9}3#cbH__gGtq4aT7TCmN>A>xJ49S3mxt<^4uD{3_QIoDR0Ti0jKwCQ^2 z>Fb*be+3@TTE}|8DU=4H|Nb5%(+lL25sVcBs=cD4wtoFY01l^+LGjO>ySm2jbyXM; zDI`4ZI42x*h&p>51{EmRW-kp%0t^jRRqY&viltllRoZ5FIKe!1Em7eFvk9_Li=z{rDI|2A7iE zZQCwtWm2RE3Ml(d+}m40+Qo*?=FOcT&^p2X%*5?VkyAeQ(a5irGMqrY12UO z`N7S_6$0*MO=+AsFimzjIk{InXfaN)g~jL85vZMh0h?#dx^<%%&^VCACv25iZ>SH{ z1}2}RngD|OfZdwizWE?ezbUYq@^3dPy}7m34fL_FMi|h)OD$_c3u1SNw*O(fu#Zl_ z)sA3*47SJ65Uipb-yhK)UV9L@B1qt48HB-u+Xo9Mu>?mFD?L%Z&tquknmainbpb#L z?&_`Y>*kKsyE}LS{`$A(1jwEu-@QCN^R0IMdCjeS;Dm7_G-bG=++b(-^foivv|o(! z%(U3X@|4K$M`{#Sty|}X9TN9;wC+gP*jULEBHtBGL~8!|LjU%CYd-R(X9z6a+L5Qi zEaMjEYv0vNNse+tTDiebNm+Rt|8P~8cFH1mFjYO0+qeBoWtTQczKj1Z-Dw;AnqF~9 z9pXWD#|Vbo8w`eP9>A=t7q_>xD4}r4{2W`ZYND@B#BDEx3qz0Y$!xkVBl3&NN9*R) zzhj8^%rv2MGWZDf-3>mHn;DK7s8`s0!@r=fo(Qj}_}PrG=$N|n!}}Q*w7m%2h&FVxn>l)<`KvaR`njlClE|U4DO(jjN;;ASY=g{o}QlP zFu3oG=mriyoT9-q&r5M}0b~IU#5i%(ZrQP-w(Z*O<;p`0Pq?2jPf+SdY%5--o6UVw zGeVHqxwGWJZsjKL3EKZXU3@nC>DfE?>vtG7w>NbA#*;I1-OsfDE9jQMC_dV$Z{K#t z#>R|urPar1X!NIqblSCR*VTlpmGW=}QZx`cUC^+XBAoo^$^CwUz31as317>o@jSb= z{y*Wztl($QX44uA?1!3G?yoUJht7DF^UCg&o10uggnjLp|MRcq@A2|4X!<|b{MTr7cgnp^@S90|wvdx*OcD6~BVsiG7d)KR_F}Q9|v=s#k^Xuub zI$aKNkcJjGV8DKloYqO%b45MZy1 z{?D^R>r=q9b!vb#bU)hgB1X~`T)uj>sRY1=e6#0wN^t)_v;N5cwj#feQ)jnb5JS(w ziKLo`1wOtpNeQSFb3&Hd4JJVDbpXnqW87iQG2fsB3Zsk6ccD*0xO|LFdFlI@W{mJJ zq5K1r^kGjK>b?1wrni@LdeWnuLET{H)IwWce&ydzsIz_f$6~qrHv$e`P56DUZA<>o zaz%d@g@6-`^>~vhtUd1Mf`IKlH2zmt95+muFyVbcK^vkD4M9IfEoYRkKf?xCyGDG7 z{LpTFynRV{uz|h6t9{>Uu9O{>ECJiP~7${`#`-jnGGMU#wc2LYNz2nIg}KsI{%_ z&zqNT=kRJJV2HcmD+`iEfos-M@{3>tQ^tIt=$<)u?h{Ir&fU9TvEH%q=tv29^&#+% zF0Ga1KKE37=-ThickGxzk1fFOIV#P5|7_y?j5TJFo z)U7Vs^XBcpd2`h6J$n*%{LSg0hxA%!Y3U3&A}%A0bY6DXig!`=hOcx4E+aF54=Fh> z1dY?5wBg-wxp88!>z@lT^U{A0K>4Y^#-vV*eWIjh)bjWHtJ@#9QdNv8Ei&%}U)Awy zIEKpV6L_Dw>LO|u#@wTJROh%0IiaWy8BQN~qVKEUM!+=4^fmx8WwHU2tg|07_EmQ= zlGd0^2<_x#Ay_LwZv)ihh)K z$)~`4m_F4K9rL%UQZp*)X)~@_)vKGK%oD&A2%lF86Kt*iU3>L9NS*$XZ;FJwiI^-v z7@nhY{I^te?cGC_A88GM5BGR_ddBCSr07)Iln?_q+@;cP{tSgQ&RLL zJBijz_=J9<*4~5{$%fcAMD|^XAbEVu$b7`Yw5uhf<4DrozB<KU3U)XD&Pd|Vq}hIV?K zy1I7F^JmXAzY>m`NC1Q`S?_Q_T)?Nb-e|!RL4ouURA{~DW8oT$ktdDpGzzhm4LCQ5 z1j!cNj!f~oOzgnvKZp{~`AHJW-txs9A;Enat9Csl+-~4m?Fu1#5n|(` zZJ{tSY}GIB10xFfuXCqPJ9%e=Am4~t(BX~Sw#D-HQG=7rmH?;o&{b7UUp~ct_xHk27Xt!X$?7XFauhJ^6MRpGNr@by?ZBDOa0~a6~&?$ za7uWAyBM_1d@|f^me*AvLO2T6CE9WIhvM7jGct-0N2AC(1R-X zK4Tp@Gg#(JzWn$Dh0|fAJ^OOT;%@VQ?&g0Bt#WOhIdfVFxh|J_>Z_bi$*ooySST!X z%6h;IbaD4HzWtO?=>(dDnle@B0>| z60c8evK2I*#fulue1dkYSCZ!>=^sYCO-4{B4u#&j^@!&fghWnMaSS2$Gu;rXQ?jPc z6FXbm9uXawyz9NmW|*#9OJul%nK=AxB!+8~qo_~Xz9n+N68Bjd1>=9fs8I`2n5zht z{fCm4-$4)rmb14xdJNX{7QWc}Ag-tml zTtTNJEZaU1@8C4mOLhAuX7fS-F`p_?HyYyW%$WyP?hq;E&C)+%4VcJ3?rNC#x<8FT z^0j?`s@ZbwSE=>cGDjKl{d&YgN_0c&p4_dn^<95|kM39K{Rp%I@MdHC4eQxlG}-1O zFmijL-o9PC5kwg;L3Kk?@(@&V^js#}gn_BGJa*3OzLimw?(;Pft6yth6)+r2L@J zM^s#wx=Z>VD|#WK<*X-S-T4_;H#dWo5tmo0dNfXNdZ^b^W{Wz98c3=iOH$f1GZhh8 z-Q9PCzC^Gd;*ZU~q9;Th9JwjH13txZ8{SKxWNl!`A6G?`aJO7X4Dk8PH-R0zXV9eK z|Kwi#FGFtkK3w+c$OnLhU+Tu+sp#Jhr@SGDWcun=S7l{oEeG8oS)exJu_F7H`yhrt z6Qwywj|Ol?`obUPvj%y)HX=K6zq5OCE3nzd*&``vpnHN`Z5@K#Ci}HoXoHF^C|{j* z|Llf`AjR5GTDEA>=B-^t(G zvr-_`5ja@m&^fduGkif&#-V=W!=8@heZsnSFPCnYs{f-h5DsE215W+KK3%%(CKO(T zcqXK0OxoK(`;9Z}EowI$&%Zr~&+QPgk}%gqa^b>-c9H)BFA*@@en;T)2B-b| zq3kweY&xA!pXPLrUQz+F2^6(KF#mCk`{(~V;ZjwbXuX==rRFFzyo_x z#|%SZ?Xyh)c5bn3@oxQ=^dsCt7`Z6)9z{kGE5E1K0+wh6L=c*32jp(iUn@2@&$X%g zHgNP6lRG&~kX-u8p#3K?Vj{38r=Cj4CBt|3;4*@u7RdE+GbUZK_WVU5@bSU!JeaPqh>C=mStA%ot`!F?csCUCY zfGpGT#4Muj?;y}Qxz!Lw#dZ+xA22xT)TPUyHuCz52sVN&R^V9rv4x~t^|W=fu7o@d zhY_IC3&OZS8MH^3!+WC-H^^T`Q8{0}o7RQ(bV=SaXXED?r01|~rpf!~+1IEh{U#Js zWKIk8A<_1eSo9Fq^2o=gq#^Z7%eYW1tkEwE;k?XiTXQueYS?SBO?}r z)Lgo%0=w^@+pJ2u&>q5LTofv+OXzG}>+$%2)i?R2F#=O<$9hdJk_ zCf&}udDh;)eKC{kE!60lX;7+lSupyF%|N?`r1%>y5}&`o(Bp$KDD)1SJ>qZe_ zKK}FNzX_uc0~hE)?0quIuLn;wgBIZ(B{UQ%QE&@_&J!1|r!lp?f7UJtMxm&+_!7;K zkmQP?GuMuA`X`WO|>teHin)5m*jOtnrt8Hw@|)6cIN+EI$rHP`2nw;%iujOrsRRlCWY^oXt! z-U^+PAW#b2k3#bpGjD!`!1W0Y(~mbfNClW!~w`F5<~Fci;UB0Fj#6SIc7X&(RS7k>XTtNpJ! z%ziJzrf)Dt2f>;n#58(YZP~&=YY!Ywp>?5P23kF%?3O?26NQi~Q_H&B`kR)+etYST zpAoI|z1KS$8UH3G1HioFXVwQL+_rc47jR9!RzUZ>6HEdKM5^BoHtI zy!_|N54^a1!)JV{q$HT*JOKj4jkim3z2G6!1wBs9D=zNPuU|ioz6qi}6dyZfA8nwL zyMz4!c2K%kW_6b|8XKt|2)QiqSGrQ4OyPf}Edo(dQIYexo2aXWlLmLrDT-RdLbgcH zV4V5p&9~%>RKZB#LVJ&d08cFt`gyUz2M>xErLYb>=chT}r#@7^7a6(N?yNCw^oQbN zsXK76Hfz4(hm>^N>;W%sG(mST@*jS;&%*WN_QP%s5g@k^S`d0Ki`jn1~Gg1 z)L6Dx(N9^)mu7+?HjbY9t6R%{wzfH#OZrkkp5CnA`G&eIu4QQO>ZJZ|n_9GLbqRux z5Uxl7Wo|sJo;x_d7y4OOfrl=%Id6tl+v za|JZPh4(@i8Z>fbaC$IBL)zwW&4yt_h~wo3zWN3Jc@a$N!7ndI!_kvS3=Ivr7-pQq zMuCrR&vmBXTYb;5Z!_D7%3^VlNfXR|n((J^or=5}DqHhnf?dVzbmiw|7Y=Al?ANF8 z`s+WYB{H|+cA7D66wV^Bz?b{VEL2rfdo0?1$NC+xJ))il!!er7F5V!DgV4NfKtPZ& zH3y!dCZs@HRJc8SSqIV*>;N6}POIV61*24OTYK@BJgmu1d8?ZjS#D^`k@C^K)RLw_ z|44FLnsB0{Wi~N0yJUR`z!4iDtX*s+5^yjv;|mSg#FAcQA>A^n9Zwe9`%>n_4~S!E zAzrUS!@8Xw9=$M=F-T0qE>XAYGMrNRpj))L-lANk=$;xuy_JqI=f&2I8)N8oog?@0 z@w;U3dA5*^TL-H{m0j13`t}ucBfSeb~J?q~@+HAri#=t_w=x#UBe;qCWehV;;K0==ylbRZqeufadw+z4`zu;bV z%th+r_hg+@hdN40J%D0~rC~xB|JNWGoq3-ID)e%B$Z!sC_5q zcmJX_gATtOR@Y6Ooy%(gg~hg4pLxpeLPlwHcXJa7PDLxoy51vMcsZHe`ipZh;N_jh zx=t8C-@Z^HRA5m*V{X!KdQ0K~*aaoORfoRwtIzGAsSrxGFNJ7Vj;m2{3ZcsVvnm66 zj7Bxf&z%`{aeDc6MB5LxUYX@KTHtObt13p`f2Ps$is$c?dqAT%vwbmo$p~9lo0xPW zkGX*GD?(f4%wrljcyKf986dnxeKWIqJQB+ch)mc%?cy%FzVf7Ou z9!O$Ba5dmW+>2}TWNul$p4Icy`}aGEDxWBCL^|KsuGem8?6Yza-F8OipKW^{2uU({ z@TD#?Y0yZ{O>z{BvhMyrYE$_Z@P`Hr^!ldqFn*;fPB_ALp!F1`XM7yu_y}uGG_A*r zDp7G@P{Xq{$TzztTS&XyLXbqc_5xk;iK4@2<&2T5OfhSnA9C$Q-YSPYZoYjrJUGFi zAqV@kn_NffpVHw7^a1aeXEp5aLOcC^wSN86yJ5gSqnEKmr6px|>14Wo|M<8qpr1=k zyLRmadZs(ylLlQhj(}xSLIy^roW_u0w0$ql-tGXJ1%9L}kHktSkF5nD5yK51Dc)StYVnR5Qn^y5>Gk&T9x70wSmMTsj;j<c z*{I4j-I7~pcz_K3yBtRJzdyR6KH}-Od6Mlvz8H8Ii#cuGQMPG6d;XmAs~Jeg|Fk*c z{%AJnwu`uYwBsHWg!x@>LVwr2cz*T}F)NeLS52bN(K2Hq{lhx3YX=#PLmcT#_#7ilZr`Er zxNoU>7(-RD%RKqkngzNC%$c6P=M-}(KjD0JXM5=&W(KBKRzIUooz^gY^X85CSK_B) zw&9o4#K7_;J7z>AFxUm=5h0=~cePxW8+g1|5KigBaMGSm;8x{S(+ev>V$a*xAp^O zq0^9M{30qYv=rkHq!WTO%@6eJ+qF+07dSOto*PCx*v~OWh?zVii8iZ|FR~@k!QMc}hbZGBC>zQx%YFjer}t|H<$`On2RBP(6j4_OsD!+Gn9vYEO5p}Ug6hY7d;9vM!C+_@`H3Ro&bE^N^db9cu`V)|vuyELyaAy& z>DW>F$F(b01}Z7Fr;`_q9`J_t($elzjrWM#3gt|ICms7wn9SHhm{BeM2IPm=}ARx7P`9!8s{2);cZ1o+uThRD1_`VP!X`|=;=wPIm& zwq-G=8H5pg#J_lIi;F2}1G@LU|4OR9t-CO(7Rt;A zpteTYcYYb@83wmL@+SjG(mc#*9vXrA;SAo8XT1o^f+)0VvG1@!NtS zR7(&)p)$^|p9_LW73FyjF0zNU6`o=7a2vrKahn-R%0Ka|9lxOfPMj}=gzATo{ z)%YmUefw#qb>%2=VsvVof zlQ))y>-n2;Fd0k>n;j7-6k>@D1K{kq`PCRE3~y zc|6S>&=U2*APm61?hFG7f|(-h+eJ{Mxz?%$) zA2<=W5*%|f20e1IqP!=uEDW#b?vau#7*34&JM#eEByDOPIF)oo%l+?i1KU!M~DMvfHxC{N^45uhMa;?)+R8M}1J5@D3tGfc#v4?gHN<+?A);Ms+VNGo-> zEKewNK>|Ww<5a;*opq~&Tn?WzIK1@u0Lt)m1fGrKqSMqBH^4s9D;>d(F`v`kebmL! zWjFTb&G{El$GQQxKA5PexUAeLa$DZE+-JRnLEzWWNwAF1j9$}pa*gd7Lvg@1uSGiak5=iH(?<|sejXU<6RKn8L9alkvU zmxIu}CHJY*M15nLO?M^K{e2+5cY9`h4;Y5d@i@R)ogNDB7?d^`}-R=v0JCktX+t#{8 zLYZLs4j=B9zD406$;bdpFv_2@G6w zt+2^sw2O3DLb~hpb$DT(eN3!&))^&)_U1fJma;S@Q_9^PvV{>_48hyK(>!E)qM}Rs z@sk}gV|5Jyd(Mto_W)6>*e@cW1=WkJ_k@~!&RP`J$OJlQ+dOTKI^ z(7p^HioV5*E3=0TzZF0t5T-;A?m2vWzbk$zrSCUe1cDY+^l`VVhm~d{Ji5i-`H(S?%I@Hg*bNqY~Jf_+4e)jOKb#F3m7tVcYt6oA!GPJ z)Ym$aVCSg(6cG7pu%AIb1iEO;nYyL#=KPHGS=#mD_CNleZe z)zdoS9&wN&1>3k5{4}oAi<=k8WzDvCRamuty~>+t^u2DoTsaTIdQkR|6@d`nlo)Eu zAged37{@vn$5P{KrQKy>m|RK(29_5fD@Shrbv-$ruL)OM$VJ(8K}Ti$QEg!?cg1Ag zI;C)F=244pS#%br+tk@2#ntl1*r#%EMc#7gr&NH>{dke;!T{{?kqAEQ_*#Scpui++ z;nx2p?d3~XxDJOO^C1iGccOmx@!JTWnqMPhJ?;u9iXj)BR1-vd-p8Ut9I~W;hCnxN zbc~(iS3S&a7HCqGX4>Td@i2Sq3I03D9}bjDjaWWII*|HGVjTdfVe~4B3wEP)gwTu~ zHXbcUgvkP)Bf^bnWOeqWsq%`qHc$&1@0>!@^v-ghZkhD;a+?Or?l-mVH-&|e6Ys##0f!(kF%S~ zLl7Mn!#>?;m?=#3o4|Rc25kj6(6s`f>0wNa*V7uQ+CksMPvamb|kO z^j~@k=Nj(qhUeJ0#BlZH3QtT*38lhU$cqB*;3G23jY%xuXxROXv+bz4D}2&g+>n~1 zRJZr{MbUI>BqZVsC&(kBq~iQZhtUW$dY%1-(i_`X*&$~$q~5ODGUqpY1p#>9e+D8K z3E)F4^EwtPC>eOMabVrL!-TmMO!a#Ts9$6RaMqp}vRV9$v?<#g(2_X`xLt@TagmuIJ)(HU@KJ6L;uAXWiQkR4gvNK6k{mu&^q}56(-QZ2@OAOjjmh)DM^=0mEo?J-4^1 z92d>Uqi3|hl%dq+kZjx&Ni703V{~f59HB_GxRS?A1rHpddUCpceT`RM(aS4s#Slv% zelK23f(9zOSMp@bZ@KDeA?7+lApqdxT}_QeZxgS2?<#=1&Jkfhhmn_1)klWWt%?Sj zJNksHt{G?#LzqF{ZlFnEV{DXU&xmzSII;?Z#l|H(b^2P@^&ezp^k#w9RAJ4CEQGMQ zQ0$})h=N;qlKJG<3J;fhVmAj&v6Gtys0IYzy#Wlwj5 zVQPA5rjPL=_Sj01(daFj{D2ojh9YOcH)ijIv=MpwY-rn0V zYRRylzh`{q*MI2;l3OV_nsk*=VqbrjtK3t+r9|jhS@Q*?(ZVfO-)~`i0+{e~ec_I{ zqj}ZD?cXlGDDyaT<^eI0n>+)oZ*M)Hv0-{@a`G?+bA~2F+%81bD*}T} zIk(>k(All-;WFAsA*9Jk=kh(s%37WyH0iU?j%kl%!RbOFeZQ-%JC-ZGZsWN4 z6SD!BNt*0Zbg25!iZ0Um34OLpz*pKZ?>dI=!?)g(T%?K>dikYhE`lj7JP}gH;_5;+ zoo?+&N>`G)+bnDE@$TK7!L{bbWiqp)-ycSih~LLF%JUU$L=d#R<{wELx>cXKk6K=9 zm>g2t??sRfqvPVWYx~JX4>6;M`uC8qB&+7Mckj%a@7dCnQN-)+5r0?1W;|)}uC2xs z0NlY=?mx=vNfuhEszeFlc+_R*l9V#<(M(jYg=Ev}L-D}uFc%ZkgB7t)o=BPQsD3!5 z35NI6{WleU`QlBL=;|QIIb~|m!qJQN#aqR{rna{2hePHY=K%1!_I1QUmb`uS#ofk3 zxh@lwcIVib7;zpaM3>QHd674my$O1hi_v%n;kp~}NFGR4pA2GCts4{+PwINg729!*@COgbUI-#tjH@I(AkT4UDN@;BXd~3FXvXxJh>J z_1*&FAlebCdgBmrK|yK6QW6LOvR+{=+s_6|7^hqT-E5fw#*G1P|0-<9Dy^9r7~D-cI+epapg; zvX^44AUDaU(Fm$Uh~@<^oG}9?M<`(bBfC<(d3Q+TvL@BWJjDNuhKwa{kBt1NQKNJ> zq!3-@cG<7@GZ}CJ?0{r&(ch>%b_T8qg0tuy#n`Fo_O6;&oC(=1gW3Bn@#lx7v##iM zo7z$$0y`H}BxSd(}3t0D}lEDYC`%MAVw)^r(gxr>bcbDw{Xd!ggPR7apX}p zB8dGkBK~++FpAXrN@M678O<#bSSIG@Batlu=jc2U!}gR!nGyd`ha%U*@tcCi{lyqn z)d#FtWiBSyD}p-CZIhzWUH#UM34AgzsAgLT*C$ep!k$#j#SYWm`kb58Fj1jr&%6&G zc5z7kCM04Plp`%=wqnJ%^6TS%`U<9mt+U>UBE8o?nyug6z-0Ox#>cvjF~>50=82|5 z*VJ9a62YJ0DG|rPCuXEyn=4tN&A5JE&P1%4&&n3zXS7cgI9m3++9r~pz);@x^mEsZ zrp5gDioLs7gIBbXDN_stDouL=rP9r{3X9YD3|?^DR}_LQ^#amWx`ph{X@m$~4?t{$ zS*68n2SIx!LJc+OnF#ih|+rRT4z#cF6$mRv6t}2MK>wC@RUxU3prVfd?LxAuW3dA@V1F5E6&O570~#Ze*s2 zCHuf#k?v=Pc0M%|X6h&`81`-zcHjyh>MC$~ul%{{|ZlDr`m`Bha z_J%Pgm+cvfm#&~0vNER)Oft7!H#Iaggs6*DHMET#=<$t}MWDUIJO+;%b@ORnh@aou z<7uu98m9~_ZYeOHUhu|v@f{zcqE8(ru|j^(?c+G}_-?`uET@#BVtBa*Jix_WhWO8- zW`%FO8-!{FRzxHHG?KbPW7m?-$|mAMXiMPRiq{hA8*IksdGKPZR_E+DGf) zml+7Nj?uU6=5!UauxQzr0r`>#% zK7q9J-W6hGK2cbfju1|7nwp~6alJVE{hXPZwDs>HtbR#~{@do$_f`jTq`P%%PGZz{ zT(#=z(n5SM7UXL{JVHOz_5N9tI}^YiyM^(zw1|9 zISSVBBJVP$IT=~s^ggAjplJMLflTd|suUaG@GL(6b=>sRrB(Ko?C z(hNLdLrc$6)DiduW=1IrW>EwaI(bJ?`P(n8>d?|QI#DQ&pbvM6raV-8Mxi4 zLIevdoLC34c0eK=J(7VPeh5LXFi66kON>-;UlEi=*kWj{#5w^ulyj^TcMsf78PcUg zb(EZ}?1AD<3^VDkDSwajSHD*gN}7HuQj#6Bw_R?F-7PMuPgYRUV7VAbVE ziRf_?+wOZN$EA(*L8g- z)>p1zgt9oy%EKZP)gls3xi5xMJJhKMrTiDYo|y9L)gF2XN%LD zQ)a2+Qdzv)>nbw6$8mX`PcJ7Jh40^4~k~=hyWv0%N(nZ(G-G(=_$R2nrYEh^Yb-V@g*F zX*UKe6=a+uMc7|AW2(>-FC#h#AxlCQA~(>SuHdnNSL_i*ARreJ%nO32;HI-k6hfwO z!RIdzmWmw3p{9mz;?v>t*-@+KdZnG5^v&0#Dx27w3`{HLJ_>s#fC_ymDe36Q{bGqy zg+B`wyE!i$u~(JhJtJ?Y@Nji5L^9w3ewY3){yO36D%IXR;U<$&VFvJWJ&VG?tb5qL z7pMJoGq^EhxhL#lnK}0)yq@&3NCxCkByAmWaJA z6e16Klfq<<8F$R<*WHCZmWzvv#StOZ00K3)okQK&^+&4~EyNaHfm>7AgIw(5feoNw z6nqulIBI?Qzn-wKfWf_V2Dy9>`0?FsB}e$Z&hD{z@sB|JMG(LL?wwJ$4ecZpfz!w# zJilRIV4156Hv}4xJ`0V)=^r>jGgAO_|_804yeNWC-54FF7}N zh>h`2_71YSOE1o>7WfoV&u(z8&p*0sFZ-(yP8fv&9s<3*?a>YsIzucHLu^ZwdyY68 zZ3Oc|FbkOEVhk{aRs<)Q!z)Ut;tXkD7Qh*7TQay>_}vR8sJ?zT7R4xLNaGqo7&@81 zQ7}z^ed+fQx%uaLA-W*aiLGyQ+?jd(zhAQ6ZfD)CFj}R5y#+M{UFFTE8=gF%2?K*( zk^+>o8(cBkKnYqrv(gito^FBD2SBEsK8blcs^9PzpDK&q43>I00qbAEloO6N0PRzw z*+&G!E}5zU;ls?_ICk0dy@Z-xcO4&g*9=_M1vzPH8pKg~;rvEWoN7#=O5xV*{hXa< z^&@Ty%`5lb^)4H|g|V=hWalO!YNrs~`{EJW-j6I7j)4k+u4-|<+DV*0&dBSJTHij3 zjmEdnl&qVG4>Pn0=@yzj#O7Vj^$1qh)DXh+h=&huaQwC-l9~fI|B{onWxumXR(knm= z`l8lC3M(tyy0&k>esWVbi&+`X-QKN;%sN#pI+avQ>tIlByO}hcJW)|_N&qcciZQy3 zfQM|))^Dj4DVYivw9Ij!=mxEi3U82+kcdqisHx-zhMEfs-h;ANBm}{d z5_Mo~?mbV5*o`i9;GsuuvS60z?qpX7*{5AE)#jiR-Wvr;2`JtKkoLwdeAI&+A*MhP z*-51zUj{8s?8d|#WNNl3LusH??Mo6HmP$q1yp!*_%t(){(zs+CR^emdL|s^`Zl5R| z2G}IAGCD+lnVW`Ea@qwkbI{FRUVAD(eQ}A>>OWFgy=<*h!|-aPY598Iy1R4C_x9r+<7m{~PK7fC7K<+&NE zC!!G)6rI+Q&31x?J>Xs4KYPrNGu};op4b%@n|*f+8Z3XM`YPY~@+Rxo3&UPP1t(4- zOmEFbrp~l60CIxRySZ_(_g9{W*xsX+I@moS7>8$po&#U|%LkQk{~ieCi&iYefj$y3 zhl9D>`Tp69)5I14hP?Vd^P7liSB8z3t%$mZiQ5msv2`}D|E`Zf=;bv9t0E)6rJ=q{ z2VNR7WC#mB@5st-WRj;Kb6f0V2W*%9VCS?1lw2M=4=4iiXKJBNTDHo@@kneVcC8)H zogdhG&UJm~rXRky+Jsia*k3JfJuQ}y8Qi;fPd;CdtBE?_^^@T(i-DGf1$8Qi{9s~W zKCzn|Ai4kYWyn)3r5|i+zP&8n5l-;IFZ11nNYm<#w`%j?iD}uWs@pY^f|LO z7tU;x++%M0yiM#)6d@&Iy$X+WSb#sW5y`!8gX)%NczO*Ese=eIlx;Nd7Y6t z=;c7x6i`+@26O5Btsy<4c6(r6t>gQn%F8|nJ@asxs@r?rupwW&bzZx|>dA&DW1c^@ zc-HMnK+lkoi-t;jD5b9G6TV8h$GS}(9eY~`cN*GVq0QsY*Aq*QM%g>qpS$dGXTevFT{kVa`x?4pooBZ}g+n}LjT)i?o@jJ%%3qSe$BP> z3yWmH!gb0c6DO2CJ7-rME|+Axr~R0xp=rzC4!HF_s@mr)6#PLZqIl&C(G z7q@Qi_^-EMktp7Nx7Cmq;Q09BJA>Vq+pb9*_vui}w8i7{drSB-(XxCK0;zZNk_8K1 z4OrPm{5y$>xvFjaX{)j2h3ek}PFR078tN9&mw&&rdw_P`jIQk@lB(H)e1wgGTPv9z zBQTlS&e|y#VgK&lZ(HU_ z#|a+HqMIe3&jG1t!Wek-cf{9_`kF$=6?K(acI%;ioTdNyEYnam0~7P(vc1M_FbTNx z(b+u9osK+*I&}RAjWsVX6UAg3s*@a5H-3Dizi{EIG^1^EP}!CJ2y~pdHXwY}#$xQ( z&tniY;dZ9APE_63_0oGf2ZraKSy8**vDR>FmQCJAbxL3zI7u3OBlqS_mA{1Bm)K<| zfMZ(2UCTQ*t%J7f&ziL8M6@zSs@w3Wudc6*&K@wc=n}M#vpm{iz^ArZpkSGsk|u^% zWbi;dcYKfAQD5e1oIBB|f09P6)!YfCk6M;2qzB)WxiQu}@Lt8zq8eq^hMu#pcq3;v zHgm%?ikf3G_22f}d^|X`hs|>8VC(O3Ls$Fxhb{eFis#da?CQjAs?3LfzHc?@>5|pw z-JmIH3_X7UImf02QW=DCrmt)R7smYA4Esn4$Po8lz zK{-NE*XK)k-i5Oy{cCsc{*}k!9hzvYmv?^Ka^-vFFJ-mKvfuAF>$no;MtJevLTete zT5DfFU*CAs{ryfcGIvk0Pf)u3x$4tVm5NC<>c)Cd%@er}XT^zvUGevZ$dC}#?3&cu z-|v;!mzX{5)MxFSw)EOVx4ql4h=UR~q5Q?P>)>%Cu93Xd^DpkPelew$QiYurHH`Rd zJP8{rWuwM_*xS|FZX(3Yb6Ydkv~M~R31!(OnL2rhZAL{Z-^C|ZKyMLu#-TdFI5+>& zQRRxG6D!hP3nxJvS;9|q53kp%PQ4vv(C+d?e}w#c%?@x3N&->_#xtn3?3?xu9p z{Yt9x6_|28Gj}&S)m9}kXl10g_n_LZw~deR-L&>8sEpvKih!U{y+ib()Z;p;la^U? z9hQH#3%VR|uOb83bE5XAx;GO6*o%0;Wcp=sXZE-6bBcRa+BFbm|$PTF)6|7|{2 z`7l$CRZvqsSij_2-DClyjpg2*BFyh)=V?@iyRs4{H|I&8_0L*JFAI8l?9SusK0Z$m zkIgzwT5wSKF(i)9^%#Gq`$Khiv9d_K6W8}QZfDsJCsHlg^F9~{7>($v$0`&j?l*WA zk4x#@MgirEt8E?IZ`;Xp+mf}UbAn_0Cp;gUeQs3ZxWTRZZ-0L+Oh(6>JOoM;2Y{}p zt9x9DdmbFKMdnBR@W9W;4K<6XzRq*#R&(SOi$6``J5H#t`sATuZyWW^N#!!ladrHn zsF0`M?i{T_uU5v@XB&Gz@To>@~_;>kDK9&E=5woc{jy0AAZ9{zoz zdu@1NU|^RKn9lL1RMlDNA%Tu=VpoE=V8+b1)#%=^%(uxh6z)RfkUg$h{ z`pWO0;}`=O?irBIfGp2dM>&p|aiQPV^no+_ZhWMdR;S&gL&SuS2L@Ybthv_xC~i+f zRqmDRf&RG1$H!aU`LNHks)KAQJY_mJ(H>2r%-jTu_8WcXt7qHJ zTScaH348Y<(7oJ=nQ_P>{J|KN1={0UjeU=DAZDKF8$kMHUAYZuNKuD_?I z{gUpyh`~J%KB;E>`xGnehG~mb{IJ7# zMR5kT^FOYB{J_+GhK&7Y5##t9a(ae_XLXdrS00?I60xQ5l-bdVV;HxPVV>07tfi9C zaVR^Ry`v}`9ATC#HOnZMd$IPF@7$Hb(!J9i^S$<~@+2nIt> zbQAIL7OOr`ZX$^!-4r9Vybbzk3E_+E6u zpYPY#{Xx~wcM#fbC^?shz1#ZMirko^oU`C|b?(_b3e^OWRVURI%=4IF^L^-rS2Qd7 z1_tKDC>;X>x19k=$3V0L^t^0xLcaCCN8y#DygJnA)xJJ|pKd=kbA*@@ZX0GEte|nz ziD|PUy}r$u>>Xv(!?V8Ezqv;u%o7ndAo6=WrNYXCi3NNNTd8h}2-F4X!JlFi4GB3f zf#x~vUZrEZ{PV&!2K)N@?p-D>kN9$t%W28m7+HAZ=G+dj-22+*dsSCPH=JkMwTjQj zWAqw57CD^Z$tfEC9c~_$Vd)2Ku4G*GKsw$?_2laZp1U$KGOXRr=J;JFR{Er0P&JxD}5L|V>vDFE>+g0bcqz>B_*Z87> z9^9iIt$rXK^{Cax*3UtfXB zlxe=?mE~0dy@mxPJbBGrzwh?MEfo3x3N>KbFNaKiu1n3jD`M#xqz z&RX>Q=j=@C-%vi1-+8W)X}|B>m~~6TbwaMbenF~TW)=NCes~T;VDYs$MuZ+w6p8n> zD1G7#s(x7y%5O0gD^}Y1_a8zRo$Wg8}OA85!3M`2%n=87B zpC6^8pK_|-_@r@S;%D`nEG2%ZVsyaS%}cCg_~rYXbN_ka{E&GJ6`kuiX%oIb9h{?_ zaAx5sT5vufDvwk3-gY3<%(^zOFPXFqG=KGCh2vYmqTq0@_tt;P2=355*4)7m{q zzp=M;09l;$!!C+XQLc{C;8Nz>$%AdahNf8)i&l#+X3Lf>m^xhG^BAg)we|%r(U@_o zxeeN`Ko*~o7AseF>Alz^JIzJ;t~{9QGwP4Z*|-UL1eUS&Ay?dg?V;yS!i@bqHoAJw z)@=LA9ems>m0d)j-Z@Vo)@Qb^GMH;gG4tHZ`3>JoN~{RUYFn9vri~b|MIFqYm0cli_iZk z$(}^?AJ$E(fEDTC2}^DO$>fTzD=s~cI&5BDl<14JqwzM&G>oyv9Cdjywbe2Xht2z4 zt$Ozj^zoTV^oM|Z`fJ$RfS6J-G3Z)%)i3Wl=Y#oSn@+{KRsj8oD)%JDo_jGiXMU4^1YrL0|*HC)Y z@mYyOWyIse@Q=>JzrW3{57Yh9Z=&tyL~IRY<0#25kd#Lm*ME0k^ev!?A9CMg^b>A} z+C&mQ8oxAr*My4 z#iajLc%zK>&lFLvPG!f3wmDf+r(VjrFLM<{JNSR?cPop^go5U?I48b-emCiA@5mW$tWEh5!U?&`#jk$p9(7Wn9UhiO6d2B5MHL0_V!3bG zqSVSQbTOj4c9o^`c%|p*SoVCZdvl|$BolhFglymnQbNGE`n2f!&?)cpLYM?C(Qarp zscOn{w((h8$$aazNWR;w8kzP{zn{WPSM!F|r@2xl-L0vP1m5LAv9a}yxqD+q@}hG2 z{Lw`v@pjY?fBZhi5IX&D{ONZm{^;2{r)|_*! zua-0G-o3jz=cnxJddKnfAkSkjjHklPw8^9Le?GQMG&@9uL||)|Smg9s1K8BpkjR8U zojSt!i)|A1uQ*!aANKWem324TxZ)QVC2oxEcRRgIL|xI4wSO>p(p=2-ULqE<3Vpqh>ccDy z99An4owJ^(9Vty#lkuRgt_#VsE$n`DeMsDp@b|mhT61Y~DOhyMa&;;{#fb+3m@&jd zX7pW3Dy7%qoF3&cdX&n>9;Btwkg{eIE8nd4%tVBv?TY(faPpTd@?Ho(FNX=#JXS`= zrY@(Z*NU;;O zt?6(+6ItR>zKP;ysJ6oJ;hPGsxGSqlgs=1CpahXU zlP!%+D1sB|8{U~1an4r<-2LLq_Qrr1^`r@9vzgYNJ9FmD^uS*Q6Hj_^U1J&a&u3sE zi#qC=!6GBKl@2f7f!dye@*%4&a5UF&N(-vI#T~}I--$_zg7)_k$AfFMz~60_s-L=gO8%laj6iLwcRRR@87Jsf$kMcW+OFjI!76 zc<7jYIdA-Afwl0*oP%;#PgMgYn`~LLSsnk%zxc6g&e(S+dDg=jmn2uyUs=AEp1+LzsvceQI?$TnARWs`UYyBAp@E1`o*{D{FWf5x)5o7g zK`(4V+O5e13KO3~-tigz;}E8wkc}t`(TU<)IIL*T`I!q-sYkEP9^0;0b<#QMMixcDjFu=bTzmOJiQ6cit|? zZBdW6lWN~2f!72-wW0Z(1$g=8%a`7J^&EP0YNAEG@_cAw;k$^+Ys>%Xfh3Y3TMLR& zW8>mMji1^=wM)y-9jD6jU0Jlx?5}IF3zZU#8lmm^Lj-<6hvvp6M%g%UobyIo-^h`g zo)I8lQAr)@s~$4WF=ji5eG_=vDmv;nK({&pZy=jenKx+TY3*!=7TkXBUj>(2R!*w^ zd}0%AO)hCvxn=*X?Vk=8&}d)sFa1<}UX}wM&I7smxQF5Bnk?nQ<#KU68+Wc{F3;Pj zXksy+z>56$izrVUZ*~X;*67x8KgWqGWD_TMNMqX$dauN9vQz_W4-UQXluvo-_bf&B zVn+P@Jakf=xK)CksLhJl6QYwLwyPWwb%iL-U1g)|e*_AAGrwy|Eh%N9fYfF^5ud}B z)FszqH0nj%OmeI*i)1u2h2@HNUgXN$d&@5ZtAR|}f$lE= zR=ak=*7uIP&Wf0bGqR#;OEuc|?&$g25Bp_ud6lzBz%QZi=LFdA5=9t`PQ z99V?Cp9%jXa(Lj5AAZJjsC_xeg#c4S4liu=?Ts3MLP|=?$wPBakMPPgCxP6bwYL1^ z$b!4tq)?Ghf*z5tzb@>JacXL6YXGRI4HArIB?IYrUOK9Ax98?+f$xg58+Yz# z%Ut?+PaW4t8aWRV$e@Qm5jreu*L1e4zyW3YjIp8MyozI0VC45%8?7!K9%Fe+t-fHM zqveJYz)Q+*4erStj%El*O>Rsp8LRR-qf_^0mvsPJ)+0u9QI=luSxv$L~ooR}VD z5>RoS)tReN{n>S`+LKyasPFC*#2fnmi2Dw(s?Kz4l1!2@#+WfGwrI-10s@Lyuzfs6hp!DguIFp@|@&DV=|7S?_w+Cq&-@MpkL3r?w0JstTdXea9*n89~vrK<8wG5QOUY*`Nc1 zzBI!YMen`?ar5>e5i_InmLHng^KvDol4({kU7c*k2PSB<7_V%(={tO{c%XU{u^Vs} z6DI1&o}B?&L-TvIZQd34{KO#F9BGQ@G(7HQxI>5xl)P*+mIeEk2rpJL(#M?k*IqGS zc4Gc;u-;A;sHjL;tAG;oI4PlF*{4@o1(8OU9<=xMkNV=#8gwWU{@?zkEAoKZ$Trp& zBGB0@aecf^+3{w7yWY3*!JB{;nh2BYx~2JU;3*)6F>o*5Insr_B$p=BGcfE+Y=>2E znf$82sR`Tv0QaTJwE!rkZN`qvInxq*-t9sHUx*2NFR>lk2U^JIz$zLFIzl=yz4?=0 zf@z1a;p0nR+#W5BMvSr44dG^DSzI`9j8gz`65HW%4{*31G{cTQXK2T0ly`c9W%y;F z1z2$ycYECL9`7;)Q!%X29=azi21IxwP&oy{L}EJv=)G8!lToB$wP_5{6(oLNN{N^D z3G`SN7l!OnrR6fyINVekA_{-t=3?^J$^u)=tIg=|PJBs2dvc-+A_djxL*vxbcdyLB zY4CwXXyyo%5CTinDf5@PvS5Lw;FxV_+$a7x_n_C!jZL2!E9W0Sy(6i_z|v4ePL0l} zbezW7cK8{-{>JFTPLF@-*1sWZhkx$rb4qjgu*o|gZM(j-rDR?H>Wd{9ayQut)Jixz z_f753#o^wV{b;$?7E6+l?mK2lhn>VWY(o9bvR9=sq9~g!Fus80kvWYAg=n6PlO~nX z_$4;ERYuH062}6`P$>=09eq9IxdU3|LV?|^kNk1)L$ZBj<*94r}~l! zAu6xIxkE6}gjP!j7B$X1UR9Iz!cazQsj_ic5;GKF3|d&$Qdo>R1?T7c3A!BQkT8BBQmBOHBf=KZVTJ;vpJq1XDjCv(qM zd|Db1mn8*tuwWArYR%f}gu0?xZHVvku%1|M$QhjX)TyefW+6tG)NNH)hD|UX2Ol)k z)hY}y23*ROTuKR~W~o7~ah?onYhi_>0V~J#{A_Of{qxc@m4P#BY(ol(9G-A_<;pKOIHtp_Up) z>t`PIEzFkpuZ6)Dck+>t zVml`3Fqq1^_;T}D9YyNzdk0wRbEnptTc^p@|6TLIkin20{KMh$vr{Z_;KKs1a%Y^bsQ9 z*kX^y^V&GB2Q`qtkKRA}z>wLO|7cb=nNV4a!CSG;9f^2MWn>DAF9-s^Q8vjsa&z1C zp-i!Cnu>C;)HP`~lWG`$19nblMHfl$K7T#b8A}Rtu-GDV3*lK1)bBlc_2v(s-XlB* zo>V#}>|()2bskUAH}KZ=$6-=pbIXxTGHYXq^P~~64u{YVUm^w6m_XRhb=&TL9I_ep zD61^1Pq(0)FZ@8qM*%70_Y`a7b2^pbHwNsO0+b)?qu{G_1{k%09j(I%oo!tv_r4KD zu*!%~+oAKk=#NkaJ3@`$75qHvaq;v`*}60(lNlw?jp!|l8Yoz zcBkhmR(rWTKX`r!OJSF8bNSL0%MppNKB2w(a;08C3nuF{@Ll54FHl4=@GnAsh#)>F zHxN~ZtvUl8!ly-CS@)Z*mR@TuLc26N0duF+u~Hel^Tkc>(wxY3xh2bXPw_=uqw%Vx z8H2C9d&q{2X)$1HAvQQPqtFf5b{;ZuKi4AVO{imeSIu~6FLeipKrI^GcLA`addS-< z9CM+Ut9zpncx zmMVbaY5z6;4dzf{AF!823uQT1t3*ac8x>ZED|;+Q}BYYpn8`H88dKWf0XM( zZQ3!q{z!24?YIE6CD(F7V>PPRBco{r=__FpP1jeV(5(-qU=+?493~Bgb#wqJCME7Z z1_EC0$MN7HIACe9DEtEOy8o8uXNl{QkmH_&pj-^{_q`HQX;?D-;JK+OQo9EWtFsvb z;~+bU6k#Egbx41X9p=C8n*i`7;*hxJ0o>r@vLiR&hsl#ku%DY?A_GwGJ#x)IY)eCM zqBCMkklOBlac}*~oltVWl%v;pt!EZ!C+;X+%|Xh`kQGV44OWPNk6_lyP90jKdesa) zvP?<`m^%^vjy^r-z(Pld$FJ5+8~)__<6khLX2&kaqcGe%AFPCVI9@y=KBW5j51(e* zY{@MF46;8A_JjlppcpaQD2m|g&1HyPnmVv-)-f>Go0hg9P$#KpNo4dkXzYwI^=F_a;;_{SzGtYf@TzHh&D$Ym$u5!_-7+E`d%K@C0P zyi^@Lrz)cC7h30BfKMgCUhEMF7I(24SZPDpshP$UwvVy-9>BfDh-mJxFe%ZgICI<) zuCMg=*SpP2+bkbk*<8G5RwTmJJ}pp-a%Z~4TFu_MNK>hGi?hqYE8>G51e=oD#{OlzC&eoa>_f6H{|BV9EUyEXedRk3xmkh8V(& zE0+7Vy{ZOapT_zW%jL+GmjG-eh)-yZRe|kah9$LDd!ddz6sh(m7Q>JS*uwBMB^hjy zA$Qb&8VwYa`N=e$box>mGK1x4 zOA{^yP0HBDrZyHY5j}?VgRHvzCtUlWZ>-`M2JwlAGNOPj^xdk#D4WSEZ|)P_#zz|f z@{laYJb7sgrq@blgSccKz&Du$M?1SCDj8TnX;p?OvSOAIfsU^2vm1lpp@T!m^nZBI zv-Rl^JFu;!GAzns=(uPMHp@!*ea17irbu%ig7T~Xg9gqqU|*!|m4^EO@KWo3)@%I| z0klU=BPi&EOJgO2)~y_wV!8T!=FG%p1Wyn=URwOh{8Ib-$R6u%>9t2(YXs5k7Oh!I zTzvQE2<%{Htkko0kX0m8eEK^d@5mBgv%dL^FL&vb?stEtZ>4&b^^L@_jF`)GG5;!U zyQAPOet21XHH^?ogeOp|GJK=e`=(g>6R+uUlcgM7-wqw@sEjc9+Unv;02O%l8WdZe z_0H#N^{ztf@TF7>p+7A(onsvwt{wR8M4wr? zCdX^!mO_SpMN3{!oW8zL#t?r`=rv&Ch83gBk)Z4%G|WAt{?Z?H;=cJ{V$GA0Q-?$W zNq`fxQ_b2q&IO|9xiVH@hXrg}3OF*`+7d9NBM|QrBu}mf$F+6n4KPL7h zqIO7aPr)&%JOw9~7?%dpbdl|Y+?9kyEC=;nbNwfb21;yI+M-!yc@9iH9b`G)w+yga z$*%LhA`oV9!veDE65sMr=9Ny`VN+YRXCSh_F{Gl=B!PSN_4tPy7QMv#Yzy;e&`i+a zA`kC#HZ(Mic{q}5<{#YBAt7_X`_BS;-U`xUDwsOe^%dFn5jm3^A8pkSNQ4Dj5E3MUg%p$Dh!x(miUm*o>)3!}MVzJMY)<>igHSQMTC;Pl4HD_Z2v}H#W+?zV^Byf> z=0lbullhY$ZSf!V-8TenpTtJTRX-;ljI8H^=-2S|q7Y-BLI;v7xP5gf>6C)plMeW7 z`*)En3Ti+Lx8y+-VstwI+pb9F>+k~Q>Ue@(SY@PN@dHtVP$zK{IDX*Txb;aMnEaRK;0PgzZu{Nd-!XXv?9VdY3j>&DKZ;rtd(=wv z*{l5a51(Cpc$8cv#Dm*Jnvznu|I8~BzMc}jDyCIgU;yZ5f>wn_@I{qj<>4vd^_9|# z_BC^eSX?%x=J(I~>uqu8exYMf3n|Qgh&ApAh9l-*<5$QZ*piXu(zLSzFjW!}X`t_4 zbN4Mmm2>zzX97=w&`VNoJgByQ6>(qhn(!6F$(R(Yl?;XOwOl}#h=@bR<_bk6k}$|d ze^G_xP1tDh*%CIP% zf6*2_3Dh7?MaX;-pYY?m;Q42Rhd=8s)JeVi90!^;f4D@-tm}oN$qYzMe~qZDFm0^+cO%tEg zqbKx`MmhQ^>_*6sq=o+ZCu7CG{&?upiWG}KUkbyz^slkziI_QI96P-tp;!MYwsXXa z`f%^Axz9>K$3zTaEinqiU3A3v_1FRIaaQ5Rg|ZXRhgvu$T9*06O7N6dpV^`Voci^% z!mGCODt&sKSl;fFFa3+93>{Rq=#N;LmsNCE_|oByms)=f_O1e7wzzg!*zEYKPt;um zpQ<+JuA{Q>iQz#LBMkCo&b!aG_8QAa+#di^nBCm{UCkvPq^OV(46#;!8cI-iOnzl4 z0L=m4#ia1%)?>$ld+#V@;`5CZYboF;TRb zly{7JbpRv-Fzc>T)b_%Kgf>UN_X0=2R%<>3^B2SfehhxBRqhEKJn=W|kyZNiUXUviRw`nZU#Ygt8B6a|>TMZYRBR zX)HJ<3vSdJ^-+5s)ucTI5J^n=F>gmu)k^I2T9$07KR(13VM7{#84Fl?2#yT+0FTs@7LPW1udpEHCyv>Ul$`*@XI5z1qF#z0irsO&ab4z34gm*d? zVYMucMVM7m_&O#sQ6H-*U&O(e7igw^yZfA}Z>)fqnW)2&_{9F0eu7p4u`xM!W1z}T zbe$VA$sbUK^`5fO+kgDNZTjH7Hi*W|Y7SlaVeHK)#zWrAn3J+T;Hdfhv%`cGLtHcN`gz^;yj&O=$N8Du;eOJ z<7lE5AmeeMP3CTVctU@sLH|WrEO?t7ZtNzYMP9ZWuKn_BjP>t70)Kg_?sOyKaEgWC zD%?v+*8;4U*p9d^*&=HpBzY_YfMzgQ0Y@>F=n>>1eE;&PFU})qWAB1SW&z`8%o#xt z$chFcDsbJF-2{ATU;V}0rQUrm>6c{oOJ9x@RwIpxZ1R;82KC5O%0L)Wdr1UJy zT$mp3@BE}>O@n{)Vo~r}paLw(?={dp!0HKpMRhG16S)tZ+Moin=!N$?);~Vp{bvi@ zRP=%u4cG#G^SghzhW0vP`@IW>cEUPKST$EP_X%2?8xt>jS1r&?z5V{zLq%KKm!sug zin`;M@Q;|-5)Bn%kpMD%v9&ajQKle$KJ?xv6TZObn-;fXg_jX11kR>^Q%zOX@%O*Y zDn_r2dbh94a{;+|zd8tx7l{#!e-Xkr=Q{aj13-+H~vP2bH|2k z<1*CI(oy^HL8yBQx@0N67dK^wr{D_b7QCWvx+|cK+KS+?<#0(bJ$L-#TT4z_){!`q zrLSq5IyOgms3hp@=zvs=tC<7Q^OP$F?5jcC@EgDfE<$|-)~xZ{q5aI{h5fK3Ub{m;Ma=x)3dDh)$6! z)2+Dee28iT&Z<71TL>moU!5}4y+%Y2R6DW(QPa)MJ-=Vr{i+&HNCB`JzOdD$KPDl> zfh{QfAItV@^fF@Es>{O{lbOdY4@R^qi_uk5@~3AuV9$}VqS_y7F&oH!2W~NmXlhaf znWTHB`AHWdiY5;n(6Jj+%?&W<#f<Z4uwEVLG!0>Vo9G zqhxFfN>chsbR%(3GIYP~v}?kXU(6u}DwUxGJ{Bf5jqNQAm!(-)sg=O~^eHs(=+kNbWweWPT`qhH3IUe~6AvwANuq)%H<=P^#98i*%hIj#Rgqg;r zM?cV{U8@0Jdjc^i*%GkG+n?ekccp0!3)_Sq$;KF-D&5U$F! z!0sQe80Xw?)Tkn)FWajKGo5T)JBEv7i5LnP8PBbF{``50g%B4c1f6N<)^lIIfugS? z5-&yQ_lUhK*l+Q!q*66UAbtBa*rsU+kV( zaVD|QKG5Ukh@V>3E5Wpzhtc((<~?L1Fb@xhMkWFJOE9oZmHrpRn6?wuty7L7uNT48WN}~PN*c?+zbq#|7e+bNG5b42+~a71uAQ79%~1X5XVBLDPS6A1{9yx!A&`4H3wP2Glji&hrKHED%4kVFioou8x$ ztcZg^FxE%ZS)uhIB;w|PJQOVk4kFX!Fu7jdAN#kRVd+8-Kq9dbf&n>W2pp~(Yss>xX#~QuhjdMn1i`N(G%$2TkIXB$U4rAMD;qR-Z_6QK zHyYr(T~ZBlP3|}H77%a@hM3%w2*ig^O!Y1+cX#6+BVQ6@b{hffUJ2_6mJU6EG-110 z6oUPL>hKs;*{6{Z2kOG*#{Td6;^^;hv3-+r5sJNUsmOU$eA|ZI;vy1%jHVk~Y&6_@ zBn9;=7375}KrnNbhCfCVh1tikIF4_ z8v%lOua9(aZ~mYgfy2AE7$uiv77lDfqechJ6-i#TghXzUgu=Qw>25MMO%XM6LJ8Z? zzXh(&_b(Oi2*PU_vi?*e#sG~tq$36wp_iNC zqc+Q?(w)RJ2zIj*(Dt0CD=HFKa|6))$4@{#=mc0q1eXEg1%S0HNg^f0pW;itaVg|< z-70*_2v3HihgGrKQ_y5%((5{crlQ?bE~4v=JxQn|)t`OwCW7D9HG(%x1R}JDh*WME zwlAxatO(GkP=0apIC6H}fGj6E{|rJF#OcA{k+JGKm`EG*VLk(KaL~xiW`sX72}vrw zs`LICHD>Ln>njph2g5biVUa1{_zNo*sA;Nk!4QUIU-FOZH3m(*H1a+Q zz)Rg09NnqEYQdLBQ0iJwL{?~w#vB1G3-&g*2H(jxTVlS@_L- zW42)vYR}4U)mR#mrjDaK2!bWmu#uLWCE&%gZ^F4)e*CMtU0cd3p!V$Z-`*D3R%u5Q zj%IFTtV1qNwpf*P0rZEah*u~d^hh&nY;jpnJ|=c!Y_Y(8q=B0H z^wUn1;_x5)ezL|MB`>hU^lZR#Q{7dCe4M_A41IUDlI}7%sC_mUHT2_AHwV~{tZgRv z$h4EaGi(x-&VK~Csly5u2z|#|_oo9BmmyzM`=EgBH4K9Xv+0_}xW2oGC6TcvYq10s zUjKai`_q0w46@`{6BBy&W6C1XhA1JC9AQ!|tDkWFYI*TCTdW@fT3kG62o@YSDoKN> zEF$6trodIHyyImsx#sVH#bPG!9uMqthO~ob5KWV&z2+R0fM73hd4)O{$|+ZA4cIrm zU5qqnVbaLvK_D(UFa%0_6IL!Wd0=-JU0Psjyt5VoAXFTWYzkqIb#)4Pm=0(=Wzdw^ zPFe-9_EJ{ccoEchrU0#g#Xu0@Yi}WN3V1T1M%~8f0>=~?$zn9IzB_PkvxKC|q*#~4 zKE@I$OJywm6Z}gG2n0l)hmUXqdBXikVGc(0hwd7;8H{V`V(11zN213MGxrt)2QXV% zVP%S9{u=Nz{WfhuU2S_6GUedhLRfHusxtz{(7tD^##6N?H9ba1wb0!!b48_tm5KKH z(|RSab?JBf+pM^A`e zx1!|wJN84$u>Mle4`8A-4%97k7;_zT^J&@K{!L~b$HtI+cH`7+5$b0sEXbZSs+D{U zW{>|x?V}qAgKdpqaz%nX*pUkd#Ugwd_?O0=gQrYHUMgG;<<%#fz(W(_vLwJqR)abc zHMi_RE!4++8Nw-&Uk2X$gCyOW0ewr3AN55Q?fR{>)o`ND4>#s4?O-O-E!O-I%*Had zXh>KQUNXq4@$iZ{`ppi^b8On{a9oq;`quMz=cO=zqU}$Npb13Nnt{xGL*|Sgp?VnJ zRun|iVGpf(O??0d>j2(*%7p;bosQ>TMP~sce3t{EQR@;aQP+O(=&)%nkoE;1YaK?G zMHo$zhuCTiSl;wZiBFSS)9<0A5s1`J9n|BtuL7^VOY^fe0wKBIU&d(+Qffi&9JaVY zb0-1{kID$9-;haBIT{43V_95)QiA=Sr>w>>a$FjZhFIJaw;eW2^|14_JZdm3|nEnTRW2>{z`%0GP$n7PjpoodCx5 zKzqM26jA`#EC-0Du?vN`4z=iXP&Z}lBPsd)GXwMDDabmCwk)+EQBx+HdgFZrqD95m zyKCM-O1xl@*#!4m`p1nM8@>vQWhVtz!N?CrflqQ_6DzZ72U>uL6_bATG8P(N4n2qH zy8jxOG>n@^YF#)h6|o%sMg)6D%1|%WJm7@neBWQpov|`<15nyIo_%m+TND`~1Mf_N zOJwqnKy@gI4Q!jXX?J6s5C_VO(0@FH{p|r|CZ!)hXwasw~-Zf1&FvX*1C$(4VDDEW;yvPyiwo$fom>(7tzML1+rN67$cP1=1pn1 zH{*qYj4BH%ZdXu%(%v71;;v7Rm+s1bq1Z#-ES`-rkQ2Ot4^>;45?sBb>P??iwdQQl+I4~`9<0HsTr1X29A zm}w`_rAqg-**Df9pHq_lecEC)EHKEX#77h2wSAx$Or}#|JJFMdzMmF~$wvqKkdzrTrdJIK1Tj;|8 zGQe2#VtkuAuvGIcAUY`D(D%#Xrj^Z4g}r2-L7HBB&2HJ)RJ8e#pKggmv}83Wv?tf# z>(1KRFs$yq-fV6iv94@N|))(pef}Q2j`BaD4-iDSQ_-X5H4?D0c z?$G^I_8c@}lu!o&^e~3e<0tsnVga`7|5)LwPb^%bfgn1*wpXM_P?DJAM=5J-Bo3ai-TpolZ?Mp8aZ<>BQ z&mG|pxw>}%;?PHrwpjzaI8W$9NUS5MN--N|xg>QV5^{>LH-XFat{MUkAFKULpMgxB zm|zJa8@cG*E-ZgjD@iItY&M#Y`aNrk2K@+qM>cF{mI&QkEV(iQBBV%22!=vvoGz;b z15yWVkF|tnf1HEEb4O7kDX9V6tvalJrZCLxcx;hvkkbO+(!4mBQk61jESADh&mmZK zGai-n-mjK|+`6)`wWFiF2GECz0~l-f^DtTiSE~=XfB{+#g-a$KjwpI5Kc_JT&QuFu zJsR+;!>39*;H@mRE<=*^8`(!lfr&ga8XW{~C0oL4QIlPciapG#8R_6pnuvn4j^De5 ztw!vl;?6`eNZRgUi4@@Z7>JGwtuk2j7VgTPjW>p9XpL$>HCwft>l_*DV(d5bZX<1i zu7yGWss;H@p3I~ngNmM-0 zRPvQip%YET31jz&`BWY-&szE_Fgw&^X2Ou_uOf8mb#omk^LF+7nmhyJzG#dra{k#@ zUd=xBtkz0E8mt@cv5&ej-Yey^mh*FWpA!SIgwyBd58qd>{K-}GzcE|krjoR-#w0W` z{$Y0$JJD<5dG*+}M@}hDlR9~AEX!X8ap}lIEgh_2LC8w*yL9ug^Met<9TVHZVw?Sg z2965$Bi*VA4e|RWI^c7%mhz;AoGxx6B)PG-8#LDu?1l?miAuHU$45i3C`l!i@EdP; zLOv6~tl`%j9O3p#=0s|`>_`e=u@a2LT%@Ej39t#{^{F(PevP``Oi{rw>f2&$>1xYH zD>A}~l;W0xgM$f#uu+XwlPJWX5-C*v3_tcYH^HuBbEHH%*rPu4G$Y&uZ|gLww! zOBmZhhhjKi?_Jar?o1E|!oz*SGNEY6Tv8abx|aai1*ULJ&JQjl4h-33me%Z-ef{xS zw(S=CZ+mYj+CTqe9)c47R2iU_{#AW)0dy{IR$dph^j8?c~~#yl;0E=&|1&gau`c z^>?3hgFR9C(u39%9XsZgR^V_o;bQF(WfUi4^;?G)q_r+l${=FS0Fy*h&WNx@G$CUbKu$|v5ARG6Yw6b4SDTx5bo87 zNS^+?qxUuiJ`04wjKNpqZdkhCUZ=w9Q`Ok>EOzjFk?Kt9{ERI_8TKQs)s@QhHLsj+ z4E5NMMrtu4n<+%Ta8Ch51aK=JSqOe5N#sxQZdsI|Q+X1@Xova&1-N>T(0n8 zjvum%sUCmnz)1Apv(AH*Z<68gFI4k!rxQ#zaeH>w@bFSI3j9SSjAVazP^;JB3gTVF~b z!-3uGDCyasw`;}zDrM6kY+@Et2qN<-9;qm+>tSsCfrFDqSnm=672Ly(*|J;=H03+99t6LR)vfEI&YI7|$itE))?oM~0}E?a_89KDMM zIF*Cw$qfWApcKi9P{_o(Zu~6$9JI}_J_9I-*Do|?!fAiU*`&^*Ut}iigI{wSiK?SB zBK#XXO+nFK6@o-B7z3}+%*QK)1S*l*~i;jn;gQWj*?vR&E2LtAOE zdM79lsSLdNE+kB=YcGU(td4kmn1P;Lb4oqig94bqsOlG)-LlKf(miC`fPlU9%<)~b zEi4}P3RLtM7nt1}PK`*co=Hpw=#AsBj0oyehz-u~6i5g{MovP2-{sl&I!Iv_j3p#G zPT+P*3I}tIuh9M{U-f5;Ed$uzMtOpiG3k?|a$Q;SU~3Rj-sN+%-qSpTs|uH#pW_2IIWrh=}PaCiK}WaG#|=&8p?fU7Kj z)Fi319?9#MDk-e%n@7Zd{U;u8>@S5g|# zg-a{nk!t(I&-a?SZfRijvj`vY*T(bU=-@6sz((0;@ao0G6mg?$_{SUNaPH~3LYMKM zHcEsQR*Bf8j+^%Kkt-1J8}P^11W$vGG+!=E#Fy;vG$M+Z2wPRLk-~e~zcsVMweUw{ zW#{^v;!$8;$K}NQHxCK_5*`oihNSGRCxl;Re+Av}7wl&KW_@8Cgi95g2?KH4@_#Em z`m6z604i+|uanF7y&hCsZM)rs&@?BrkC@tS5Yz{p13L7c;XbX+bY;F(SWl#@s?Q`>f#~Jr)XAyf!Y71l!=MaCgoWh` zl=2HvgyM<56NciDMN^!>znt^daf9gee^-}%r(~fsF7$;1LmQR5ieEaZcInZ2@lfu0c z2TW-?E5QNk0T3x=I_i zOQ#kBo6c25EQrOr12!q_UwC+@&_Z|b+YJKye#T~OoKu@0C0u^K^W5aF9RzQ^KzNnn z58*k*FP;7#uv2bxO1M1~TgqPeX7c+|xg1>)mrKWzU?)#8y#DLWlm%+W>nOuJdu{A= z(PSa8H~}$(s|7QJI|$kSNx#;`Gp}eL&(%ZQ00c7Tiu&7_WDt<>AAEL-#Ybp0Ig7H4 z6nL`BY|axUs*mlDKa1)3~> zSM)0gx4}(y99k8zlI+waV9u7LtY)MUi;cd+qCIaTox=NQKvX^IG`@6@Pin zlwU*ffc)=&??hdnaR*_%roAijmMWwX%}!m93aw+j}0t z4f@T)>Q|bH*2Vh5d{G_!q&P%!Q3@q(IY2ua%nmEd_F|Y;t^@0BuPqmx%g`wgF1E%y z=Tq524EDUTufxmDZtmpJts`a{Y{6#aeTB?Vl_wAhK~Soi%_Y7{ z{L?G_ldf-8Jo#^jo-ulJXB zz4`NBc-+h28oZ8}XiQzw+4ULeVCAACmOhw1oIgVQRA=cXF-5iBCYK8c-*I{Eg?4c& z6HcSY%|=X%co__@xG|^#^p8}6hRer52pO}Ed!&f%2x9XdKi#PNb#;h{2V;APX4y#R z_%J;~ah#0Kl*;2IYeQ$WFpPomeimVfBu!X#!RO>7YGg0;gUK!V8y}V6*lUCQ!H{{> z=Eo{!4;enJbB6fl4ZI`%=%0jXha9G+z*y%a3A5!AsQjhN&;M<*6zl^tps*uei536K zt{MRTj@b2v>B)qDx{Cx@DnryL724XQ{2pqH|F!TBTS*u@G~WduRz!epulnIqBX45a zZ9gj39zJ_L^U#NfA-&V%ma~SqQSs8iATh963NlSQ36Y{pulQZn9BRo`Te!ej{2+B zZ%u929B-*NzWcFL>V&}b^_$iOlzCX@G+YUN-o9+tjmJV*EPVb{zXh-Jap3{^AODHR z!@Hk9w@Py&SNzm3Kj{7Wo9kPHw#r{3t$LK%^fFdc zbt2u()Qc5X8-(|I6$Y*OT~~7g?V*zMPRAI395tvtemoVJ7P^c3C1cT@gPwD33!;Oj zMD;bd*dU-wN!mD8-mG9k; z2rn0`_QO?EGezuTbJW1Jeuf+57=+iFY@B9oIMtG0K>nCg{irr?q4!a^U8V3T_=Z8H zC5|!W-zq72%)?QsI8AcEz=3zOvKDTjBo%*-{M>)O`2ED+m6SI7Ej)XoSmA0nS$4Uo z;ziimDL+RlVx~8b;$N3{{Ckgk9lm#Y!`w^(zdK@}<}-W7Eb+Q$7RGj*QTFFN#TASY zV}smj=k#-my;i`U>WC{H#$2z_x)TjiATmCtlzn9rHA!g+bu7D6enkyI63n*{O%$RK%96kS@RvG z^1+8Z2Mafcr@aOTW7V9j=U^Stl&|IRtpR4vI%^se3fA2`b4<7jp00ERz4qLWfdd9) z;ml_F&EZ`iK9esmyhixhyc(4%st|b z2K`Sr(%F`x(R@p|y-!L>kgJoElj0JV^UZEuKlU}B+z|ZE3}N3Ym8Y(muxzoo%J9Tx zL!6w5ucBJF@a*C@v+{FSyOiLJ12Lt5e6XYTz3%chd;3Kg2fS{?L| zppP(u9x~BPczv$@Y+(1h%?SsS-3$KsKIZX`1v74mi%BWB1T9Ny?-%#z(PO29!^k%q zVr%_d1AApwjCp8pi=om^^N;O0^Yu32eJM?{O{sltvup*2D?Hxmj}86#c-)g| zDxZ2$VQ0JwD=NE9*MfaYc7O2WQE}sX=^i!LFiypBr`~dR^2ZMkCSqj>Ti>k5aPj+2 zUF&?K|M4uC>6<(IU8)-?0%81s2tH6Os0g`W$IZ5^rD01jt874B=&4Eb-H$Iks1iuo0uIQg#JR2@6Q`p>iz-w@j;$p%J-u$ttY5spUiU{9p>K8gf z2p!~~lCkyP3%wxGCvO`dcbHy^0kzFm9c zoe?Xao9xG8I8?gqbnmxk^ln+`nH^?BO!ne3;*(0vJA8e8wa1P<3C;GtzKm{RW4ThaJ{piPrh6}oS*YB6kF5IF7r#_LpL9n7Mbk7|Ka_`rB8qAd8dcn z4pm_x;K>9H;#vq^BvKeZ1O$pXS5m6~;w^7KwK_7Uyf%h=pFduI@4BJ#v25Z`2x1O+ z_DJ=@|LjBvpDF&)C7gR>gRxL3)&euJ2xorp679z8ST5O_mq@!7#014oC8GlthbJhoW%N~ZyQ z6Ku}QlzaG$ob%_;FaAhlASX&;LFz|!amU!;)d*&@7}+UAE06rFxR`KPo&K=Y?L2^Q zp2)EMHy^J&asMRjk#HrC*|Y`XL?m<%dgv*4wjU{?;9-MCRTa)T7&Gd_IkDrh@`sC7 zpuRsM37_?67ccY@W9jl8p<#X{S9uDsGkw#d@h;6Yw1`HU$h(2Q&LVRqU zc{LMoQGe6yH?JeUI#$kA ztdwD=J#L)nk`dFn&i&kJc7uE6KKVqviEz)sdnMJ!q>SJBmPhFn-%SlQ%U+xuB0vWJ;=0T0g4|BgL=o zWf6*1hVb2|skS7=rPW4!7}?>xj?Uo@?mpIG$%X?Cs;?zlbLVvRCBi%7B!?NOds$BV zhflT%ImneC{=E!Ses8b;>XNA*F{T(1Ry$U0<^u6G(OUM`vipx|x#aJhuOn`va%cG@ zUA}$k@q6BKn>Q0a=-{D4@Iig#4bfBP!IK~sOD($DJycBPre2i0$MCIb0i#X?J#y?6 ze;tpBiCLu!g#*PxU+kc9xgg}+vhT!n=CY66i5*^t5X|w$v*VF4R`4b3$_#}0!3cN9 z`Y=kRoZ%Zx;0uMH$jBd|U@$Zv#7fvV`y_mpMEWM)#}&4|wPRv^W~-Eqm6h~ZEu3Dw=sGOs&`M&jOOrvO6_=YLJ{2%NM`JM2u|KOg1b3wL-Sx58+ zYotiOy|yE)JutT8VEj(ye+;1+UMo4@ZMr;a_4vtAOzDmc%QxQ;vqZ@G>F)k<&hG`x zB8Gvy2a-~Y>~w1mz8t(j==Nz>v+v+1?i6_-esbY!U#2v*=dn(!7bdI9=}@4Y@`6=s`lezWHx`e!c~8Vq+fxX9YE}px!!q>otUUum*M1jg^xM16mVHk*_(u= z;dy0ceGZTE!U~=W#NmLp(|6eqlKVgP-o;=-$zvua75~Y!wY7UAraqF6wL4ol@{xO= zIK$=>imZj(M(LpdO~C(ra!?V4ol=Zzd3SMfsMTQ>1mCBCj?2G%%W-=ZoD17=~y*UC+f4+0zsU&s={p4vyxf3zvk2HJ$;q(rU zh=>qVVqE&q+aW6>tL z_T47B&8fS!mcLx@Q**J%V}TG^iA9CiSTSR@69>8n-+vQT34wD-I@UZFMZFYh2vk58 zO(s~9&-s?_6HbQhgrLcBxK(o%QB#I8yaEzi<5C57Ca=vZWlaAQc4kJ`L(WdC(H`|__uRM#RcSlcfF2~+*7>@Yo2aS*d$6yiw|6QRI%fLhdB#2 z$By%@8zw2Nn~;bQ^pN9)_5FcTGeWI_n2sVc5@5D}wr_rn8uszI9j8~17FLAwIRf%U z=86)}XG|myLd#Z6B^8^EZ%TqCj1t4La4}}bojp7zlq9-TYTmL;xogzb)rlTEILsmD zCC|5jdD$rHKl0%z_n3WBCR1RT)`#9QgYsAXU*#_Vju*Yg7w}m}ehwo1BtM#|8oGE?}b_~Er3=;Yae-G>(n`65bMVNv)g|5W%AR~Gyu_cEKl1(7S~ zT(L3g#Q1AE;;-aOz)a#hk1#@p@C&j{fr=G3IHTT5DFU_;%T{Ws0f+2K^X- z#?8HFDB@n>el&yL0!aVtUvf3l)nC_Xa^>*0Vn!5ycZr`A5906t?r|@TH`TFDZ+nfZ ze3;#1h>4g}mfwZLq3${M|7q@B0p3?S@?RwXxznD`Tz!L`iKUTkL2HrFPA^l^{+VhuDOrm*?*;8#`nB_efhg6QT7%xd-=7>j|v`u*v6Y& z@G~G!dlc1}&f5im1{~}vp~Nc$S4zK04oocFSJ9L^LX23;joKzonl#D#6JeM$23|m2 z2b<35+&TZzSIbTKsTs@;@ShNmy^qQalU!6|p3ubP{)A9WUZ+t~`or;c&ayhVvi7ID zvursjv*aTy)MAj}?v*G!ilRjP4?K$gKdi>Qa!D9kC7=zx0v5 zsW3jyI}zuQ41jtj_y92e^}Y0cMuea}l-IKn~d2q6LG&$q3?;f6hJ8!EC_+S{uMuuaCSt`17U zeK<0H67XxGN;jIlT%|T*0{}$JSQao;-O{<>bN<$iWcdsJ$r%ZGEd8RQU@!Dz&M#AGxXK z36*^{pY2yq(P)8Wc)fc4Updsmxs8y)yTcyYU3(=<;x?_fEy&E=b8YK~)%Tjl#lL#z zz1FeYLl^%kO{+B%>Y3u8C{>s_p&v|Y~3*J@H)`o&8ykW`ESy;Zm4kDeXBK?d_h zGL>iZI@Web`Ui~=qq9~OTS zec~f2fWz}+pbKYs-^|n$g#($SKaO)@M6n)mO3xZ#6mbV_*J+yM%l5RO@ z#16uV9vAgar`qm8%i1dqoenmnel~Bym(9f^G@ z8nGjBL_v$7Eqkb_bvfInU(}Y>W44Sv0`H@1^KMyz`!3@v6^5~sJ&g}QhEDg7?|T+g zBToVDDJexC>VlOxCqux*2{O&4PJu1vJgk|Ar=|DAzj&HSGMbLtgRXE9Uaq{1CiQud z%WVB->>G?#a8)Y9Vp!|v-vM3d)MGQedCZ-vpSfdF-3vDyKI;StO^KAmboQl!2y!qzKMM@HZxm*ESaVB_H{14lLXyFv*?%yOw+L#&MJf*KmX*G;T(l@kKgz`iC;gOC zV8PVPY>aL4KAyv&{<^OuX-uYE+==6IY z`P_NK21(x4$dn!f$4P_}0oQoWJCvd^Y2w7%dy5Q8AeV0I7vPz)@HWLsaJC2}Jc4#W zZzY*c`mX382eanFg(isH1WG6*Sch)raUD&89S0}|!nW@y2uHFLUd=f44vtGovZ(c^ zyq00=#gPvS796Txi_Xtnl>@O4@YRxW@~jpFbZ5&T&!7S`O*yd4OzZ-$&liJIBIYTV z4HXusemP1=x8J$@B9;FGHeBNv!i~+t!0QaiuClc=VxU#Hq?i2-Nu0KS?G&t+F#1-mz zRF0Pj$NXVo+d$ls8(dc0(O%SHmX{A|UR;U#+9Wh!);+A=GT!CkMq>)lNd!f8$eLBk z%IYz)hN;vcsIJ!;UsS}sZy}2bzVf6`*lnn~JK_N6Qpf?_nfBr5`zREf?zXnBEIpm= zVNY}@*usjQo7J^*9532l}+iO5KvE;%_ZkoEReC!--y~xRmqJD9hNx9 zKwrv^7NzhC491GSBTateIhU@_&590wIX$|)V17g#bbwGK4cSG+~bB1Y{uca{glYYWMwwX#^_uZlAMNMfgwP-A` zLujA_#dVkFiR^;5jf1elmExz+B=eK7_xxg09#2Mv@i6vT8L;WeuQ2?MyCAx|x^G`y zoJvBuBFfhVHPC)Su0D5pwH<4>FnEaR1y!6%oWhfQsbu-Ay!_1N`;dUT z`mibbJm!6*#bs@@K3f$w-W-lv@h%EWQ2`TT&?Ucoy=zlpD?b_LzEDDGV&O!@iaLsEQ;GaZ zqW2ga#6iIXP1DHcg^ae+penYg!NWC_hh?1(t6L~$LS&jrBi);74^R3MlN9%{SP^Mf zl|u)dIUorKsGvhLA+!PB{$j|tUAV`?VHV_E{zi*AmC|R09O-Y5j&1@ zi=3CZ3d6AE=a#Q7S5kxU^NWacBzZv_{($I5Dh^V}JEt`)2`8BNW2c}J1RxkM|=3HcwkDBpvsKy~G zDXNG{-|{l5DfgCcbgEl!?W9IH<1NAI$|FtYyT;Q`+S!#k>z&>?aC|BBQG(q4)&hjQ z1KqM!1~^KAp9iC{oPrPbmURbL!@QXaD01Pdxqp+NRif0jlzXK6=MYt;SC`TJ#y~q# z5Lcs&njpKI#8VXS1nTMG?3yJXe4HC$Wsl*#rS=q?f2~tGG>8tNlYbazz_tpaV)n9% ztQ+X-*XU>t>oDC|i9nzZs;HWufByM_NiJ((prR0W`eTBjP-ZV2Lk-zpeO<`c7=JX- ze$}d4&5l~lyi4nsX~Y!EI2$-u>+ZM|4Nd$S)O2b6Q%+>b+_jtU&YRLf`9dCE#t?%s z(voP0B{;6p7rG$hU|M|qDY#v-zo@lze7f6JhI)|Lptx*dt?xw}=nMD9nPklk&oAES z4>|Vs)xIWa_^RUz)=f8jT<)8AEa_BaR!@)e=|0C&AmZg-xok)Z#e5Lcwc=Q7{aLeS z;R{jtL6U^4sNK@L_?KFUzTC#TP|FzvzBo_CihJB+?QNcE(p}@y7qqR0E6bPD(A&C0 z!)Z-J6C^d3^rxI%Iz;X+JWOAOBb^v?;~+sl3bZjSZgz)o6^1}Rdhb=WnwQI<9d!p_9#fz) zSWko6i%)Ja*7Nb87IF6az{cN)IYJs|Ib;#_W&V&VqG*F74*)}^#G^$hQfrRe7MzL= z>{SeePHGW8kCR5i_Lf8;*zqnMgfYiw#kIB-b$IwqdBu2Iu&5G2hMFR$h!Dei|2+U2 zSU$2$Eb&uUu1rZfb?g>B3BEgzg>{6}_jx;|M<^m}$!&1cH4!VeKZJ;^p3Vkds z$a0ylyY{|M`Va@Yp%VU=*o5nLWgWQ%}SY;?HM$Y&!=zIfCRZc=k zVRc~qxN$Es>P#_vbU9pj!dY;*Fzu?(t4W^itvrB*5=4k=#$d`cJggKmI_WKDUirY8 zJNoVM*0_9T;W{3Ze*VIRs*D&w6~U#j+RLf;p`!0=^Z5EUx-ppzbc>sy(QcY|F(!9i zN@R+9>c*4#<|NQ67bXYLHD%r|#mg1g)JLeu?mcc1DTl>DJi-LoRkyH-(<*Wa?>!o`>v z*I+lwDn`2&N}38^UG>WjHXbI`g-*OtALliUR*pMXOQ}ZVUS)5U0$s!^2vVJiTnkwk z9<>SAieUBrh9)-!4wh@#rTA5D1jm&cTi6gP;i*B+<%7(87i?ixLv21Pp-@iF?X69Q_J>mRDXUh7MSnI#4_a70 zKV-vo&Vh>K&w)7%v?a6#+0C#yeh7}Ky^O@hr-fOyFDOWWcgkq>jjxQ8uY9kn*R@JM z;G_%hNx}9g9n$h%Q|2{EJlnkK<=gb{E&XPiz8g^P?x#JtF7O_n$q5hs}L+Qk4isc5?vR}lneWL1%mSkj)+D<_ zDqT9x!;dF$(EPxcyCOz2j!C2RH4Zbj$FKsa!O;kiBahCuVH=CEp2az~42M_ACE-tuuhGO07iVe>iG5an3*`$y7Sm})Xlj)4F&AtmL4{K7)Z z5xaO$aQyU~{K$+yp!t->mCevsIGKSe`%T>UP42)=d}>OcT*J}aM)%v>t2?f;25_54 z+Fd22y`I@uk$ z14Y*?4;UPtD4g0%)M4>QU%K3+{Xm!+bb$vz;6Rjh*$;T!Wq&TjNgfZCqoCY2RB9Ar zvIlZRC*edoaAN_U9j`pYDr0y^I&3kIVv#yRn@>dwzb+&Gv=$EBj~e$yh`SxJ4a=Yr ze1{^uI4{&8*nZ6tlbEK#*pSu9&2Y$vQ#9iz^DGJ{oc~)2*EUB+hryJ|>3X+elDx$O znz47=JtH3v;@%JK@Ba`y1{G1As%WR}0$(?ys%vR=BD#4D z>rfLAD-2*lm-(9OTE+BJdib=XHLzomVXX^58sXGc4Bi+BHaQer8clG4*$E=&labZ- zRxPH5fLW_E##RI7U<~ATZ3^8kmAQE)hE&GEP3KmTG~pBsvk~!el%$5ifBt^DAsh^} zKZoKMv%Ll1mCs&QPs z`1Eh5v^PFgo3WP}lIo1#G^rh&isLyCz=W;@%pog&Zq>0F2j^Z*U8akJzDrx{(s}wQ zO@e#z*F(;>7Et2|_HY!_zo393CyIvi7!v0SS7>9}#z4!9=a?IZ##>+eftaO_JrAj% zG8+#=lq4bLuRUwvp$aKfCs{F)F-xYO8g4(E@qpZH_mnbnGdwI%g$t6UoLzR>MrgHS z;UZ(TqDm=1&>5?R#sx2nwP4Lk1a4EWsevyGdL<@$E0=NO(wiB0R4i%&NOYKzzlJmX za6G_+Pn0c&CeerC{DHBItfHF{(|pHqrkbzuc8$GrZIcecXdB)P1j1CeP1F5Li2M~) znBy^gk|6p$JJVK1t#^~kcsR%DfX%B)qG-bmOM=obK($jiQWriv5~5$W$nB&u+TzGEMqJR43)(dh zVr(FQ)IqBL?83pYZEmFy0H1>rSmfC`j!xH}eTXyO!EWVQTd6ogByf7qDd!zS5r@90 zubT+LQkjGSNyz6oJIAM5-_7m+(e@>9HRkWzXT}dR)@f`ZGRc-CTPaC1DpO=@QCgL( ziAwu+eq#`AL~3khOIj6LwG7o!Nk}DabfhSyMT@rgx}S4S3-f#b|M&mC@A>$o&hnh+ z`9AmeUatGTuiKem6ynn+J5`;QrxE~j{-ojgq&bneX`igoh?25Taept`ifHE!O*Y{P zG!&&A8yDF-au15GnWi>LwqhNW4B?-#phROXiO?#VkwxB3nEH>$5#|6bpevk;NvagY z6cks$r=WQ!5eA7%iU0K2n5oTw96e3rod_BwjR`NbT_-3SW(NMG8UpZ^q~a7Q82nPt zUAlL6X&?X%Vzee=bpN)t4@-{_p)+ZL%P!5?6pWWDfXO{NAJbm^$IY8npm?NW4Ky+>AnY_Vwi;aM z5lLBCgge&NIm9rElu3Teu7>*h4f^`}ls>rs@xa0GvpX!`Lsv(fwT9#K4iHJ%``!0C zlM|}#ZE>{DEZNl4w9JbgET9Z{DaxegK6^2bZDZNPm)n6v})j z&Y;a2Tsk=8=`;r@NJ=i94AkUdwM6rSpMX-Dg947wMs)F?vQMcqER|fhvx^yL13&67 zx-Ba0MK50L#3aM=?NyzmB2ddf%)vCSu(7#rHNv{R2M*+6bi{KcW8fDoB;r*DCxR4| z5C`M?&$-psu(^KMnJVs&&CUSJ*cHDq)hthoJ2hwkn&4rVNC}AAAId)vG(B#0>r)YM z`>CvR)PwA~GGj7ei)nV-Ik=z}_iBhq6#@M+I8VgRS$^&S0uE~E^8?{1GoCjr^jiUf>9NZ0w%^Zr6oeS|yb= zbNQ#s{%x=;gYvfWf-*4vQEKzZW5(SV&n8RU z-@VIzF!bmzf26d6A2DYQDmFtK`^7<-j@RZ_Fp&&Z8%m;g;rvk;tmb#kN1mAu%VZOt z4lATeRw{5ARHctfbTR7L_^NM%vUk+t_6P;wcc96{vuWTn)2Y(>(^u$p*pL>baHPHU zR~S=W>}k5t;7zD^mlyIXO_s>D= z4jsCSpgAfONAnTpnei$j>C#zAXTN$UA+TOZ!VE|S%aGrR#C!lC^D9akp&G`(^>ZQQ zKO2f@>(n)rcICXcBY%k8Z9+d()-5O_?p%y+ORHtdF6j{89Im^wr2tzLn&axV`#YHv z=z|HKgq2whaGElZgk3V}YtDifRL{FUGZYI=RhD^ z$vQEQ!+!~ydYM08Zh>4tfT)OyirUAqhL^;yY@%C?$nYE@ASAcv(p)sk!;y8Y4V>hw z(ENL0H@IkRpPe>(LFpAjd)1mTLUImG7P)cVYMq6i`WHY0%v4KCQ6e_q4bBz9<(DBv zF-G|5oxWEO$rq;4!T{fLXhs`G@O9W-;@U<#BQ*qcZT=TKoF-dJW>DcL+$8Gqjp0-l?T&=>AP|wJXx%*W%9R}# zoXNoqq4EWN_g|x3su$9P9Leo>=c^I)fhrR6n-_PAkpA|2i?MdD&*BMhIDCa)#gLP5 zppaK(tQ5qrj%KQS_E2}#)P?DcLTSKgVQb77c?Z>1I}GIK%n13dQXQrUv#bGa*ASuX zvu6u0W86l@dz}0snaNCH8Cu;09T$FAc_oJIeY9B-m@qXb(N2ZoG^gnuvNAMZ!tP5M za%Y>(6(D33%j>1OIFc2o;79IL`Y_|?iIfvD04ptVj)xbGhtqBVVsgT-{jO8a2dPb( z5Abg6L5H<|JK<}{A<$X~etw&v^%$ZX_JneZ|0%b0Dd(IxfqLs;^nfvH7eTAU?s;~V z8Hxrly9KI45!S7Ey_3{*SUBBBx`<^+9_yKq%hQw5GY{vpu;pAF^DgUSziZV#gf@WYVY{Kt+p zm*NY*kXujUOob;KFEJy%C`XAfuNsJM9yuuh&$M!+WG%}&{GO0ChDqCM=Dmrlt(k-pphUOgWcA@k-dl!%>I@=!Psq z+LO`*RINkmn0$Ro>EEEFqRq|J38h@POpmcP5}6nCQ{oAn+u#RuM3SmbWb&6FpeVzL zu};)dT*5%tmDwSU8re*P5-@h*=Cy0HY0_*9h_M@5?-5~kJPHT=A3Vsb_Erm1 z|2`<6Y|54KuAy7!>gnrWAnb-z_^OH~A90E>ki|J89_^61P=Z6_y5-=I#wR_YBt=dK zL?s`0$XFXZs+9SrF6*G9@|o3X2HbqCE#>1$cjcZLuLR9@svL9G%T zBFao90ufod??tHkl09g#-Ss%|G*ZwtsBuADOf%jNKdyyOI!*S`#h0#M4kU6Z2WC*H+^15gT(9W5KyqSbEY2iz~8x?|xQ=C#o)&Qay%&D@c zypraJ%VG2kO$oS&NRB8%7;_pb5>JM!#RV|eBV5d9!lTz9z*<;G*c^=mfVtn5T$zjD z9&K79QwWVo)kigl+W!6fsT}JqM&gy2=l54!g%9ziO>VkYFEnPt2yw^a z;2sbuKY-{?Lx8J>ZXXxr&ZjBSQ_9`J8E)QGm8s@Om>f+HrnsM=A{wJ;UP)OQaT+gY z{&B777pj>!Bi`u1S+y0nSDgGSD$b!qDP|Rf)vv)IO5zi*LFJJf!K@+(lKpGWio0;?&{Ko7btbOVs9`>V61W?LKPD#*}6Gk8gL zBI4(`9C_@Z$jda8xaG}w_k^W=K!UeSC4Hs;v9kFu8g{#?YtqXcl1A81IZ7APoN*P; z$=a7SW8WtyAQ~e5J4!y@WeR5KR=#r#fGaAOPa^|zz92(S6+lQTrIj?aFU2&s$S7pB zIBzL!GJJ}`Da>}enHw~oukq5OWP%5m=&nMGJkx?OjBO$DrPScH17VjmT#)^pdzbBJ z0{oVyf;W9BFlrGb+YIe`7LAs|EaH+WWnJXBVF-BS<3?osynuSq%x|PRco`OW93=Kqm(eYp60pd%z@_OCeJ~Elva+!Ca~YTyjvDT-~*-7yr#=Wh5fKh%?-IA zGKeqVSO7Kk??Im9xeQ>o)$NZ78U)xrTg@7y72jc|V$>_-(9graj`T@^9hKnbaVjxG z-2WcULD8Q<8Cnu@inkk(s8;~IxwwOHiiFwGJ&qJflYOstJfH)4K@lP4H*+E85_JRx z7ba_L)!vwRgYq6r6@j^gW7;`jJ1oJ$cIOhEzpAHKd^$$NhwFnV{-=kM{g56tc2sc3 z&%rCHWXX^C>hvcWRv_c$-^|HNZN0JB_WkGh`xOvkz0Z`?szCM1E+7zUGLu$Y(#$jQ z3+m$lT!iNcLCsTL0%{+YTj4l~%{dG20Q59Rrq$n-5OxeYHr8&t1b6&>W#7THxHS+$ zWF>BXFd+Q&M!nU#*dCbgsE9^PSYG_z(H1vh(yF<)a$K>Ny8z*<`H?#NkSeB^dXTfE`INJIUF^o&G#t;Xvtr*21$!f z2z?+`129RNdub@r%&yE)ICEgiq29Oi)?~vDX#SOk5xua{ zxSHQL&o%*p2g1WUgq|S=+0Y!nl>?O^Luwxt8aBY+cfl>3y2{Q%vFXI7x9|g=z}+;I zBgLU(n?jKFNRdNIel(AXhN$|9Q*<_v8_Jmwo%A8B8|vQ1Ph+?jDiqcHG&0X%Qbp+- z3;oF|wAo8Pm(i%9uy%iO!l`L~uPlbVpd?Ex$vlxufUI~n!pZHnsfg2ax)&XET<-_( zVvfH5eoHr1wPw_O#q3K#X)iVe&Ca#2?fOKuC0Qs;dPD=$ic=bF$wp)9UPPE_J`fR_ zAhh0((QlvWzEC|)VOJN{U(fc^qV)>xFizsxRA=(hi5XEx)1Px(a>ZUE4C$dNF#B2t zBwD=*ghf2Yom!n?cD=lml4~(_)T0i^q3zy@twBZDAUz%#;6=kv9;N3WIK98+^Dlh#3ugz_a7(c z>-_ZVUA+s2l7A2TvVXzyKNgw#i@ka2S1DTWrD3zjZ?2x&yVU61T)jHSqU}MSAOEsI zLs6q3$1XBKn))jyMt3~} zdB2$u^yXd0?hhYikvjLM%3$AxzN>PlL-YPDBt%}pRpg+lT3{ty@h*ivx?_$C=rDf? z5lr@KL~Vx^Qs1g*IJ)2S@u07V2k@o9HSTV2trHWPvZixMVLeWR;cif+YVM}2XH&{~ZJWc8Jzsm9OAoM;48csE1ji*FuX(<;KH=aQ7Z>=w> zCOfK24q8Smwi?a34MlD6;}S@)gfFy*8S)>7OOf)DdRA04nF>r6b`eXoE|Rm#%BUkf zV6ynnz7=L!YdmKYB%g)LnL@LtR->qI885!wcgOTj-n<%00h72tSi!~h=dUEYJ zIdLRfW|F?8L0HpYIMFUI30&3Ep&R?5S2s54^No*s9CM~S#zeVr7mr5DM6ymYsjP!3 zyqE3IGC`T@p zI#(xPrrKh~)+^&P35dx{a)Sil_|%NM7_MBdf)&#I9en?%7ryupmL zzOJ-&${7HFM5wMWI2N;sqM98ZUd_M!qNulJ+YdC6ii66bpWuJ_cz6sOw{n&uiq5sN zDqBNjwJ#UIqN*q#UXXkdCd}eqp^kr7&*R;S6G&2%45EQL#5PcsUAzJY$(-~E7$r*H z9EY4yI7k&+rn;lzyEJ;mOI@je@G@LN`6L33m~w@yxnuK_cWl|GoIxWF7u+^L&Q?eCepOR?R^4V1?O#oV4!TG_5w2p#Ex= zJtsgbj%rJoJ)QSN8dlW9W6!~ZzEp!XS;|0IXz?T|s@gaS+v?%564g)(Ih0lM|32d~ zB!4b~y2?z{U$?28EG;kusr=nXAPiQR2bY1U*bHHhHw6jh@6c17MdQIqLoDo~ zf*B5cmYG6ACGEAErEcne1H86 zUdE&x4}c^Yls(V6kB-xq71fEh@_LOpDCLG>n?GbM%2HwPt*+Txo~M z^viVsM?3(SgvTDl7}+M28{b}kqQ-=xttND1tb`$6MZs zo+K25N)kFV6ko`QiBUmhYn>yZE;Iycb<{KD6u5{U&1n#mp%LerieixOa*c(h7^rYE z)3hjtx45_;O1#eJ^jU%+PS}w4_U{GDX^hey3yb=D7A+`u@gRA(LQr+E`62*St2ald zWMOKRjA^^{_B&&bqah}fBn;8|6freF^(}7?0T6Cswh~k`Bx;G()imyO;!2b%?Ju!n zZ|v#1sSCjnJWS4(qV$D}x&jPke7^sgdCh%-0WuyomS_yGgji_BJR)qjMQh{oKt^q8lyCFEb@U{;!eS%^cdQv(L)p?o7ZBC*w zZlnottE-?4Juvkw^Xvl6FOsk@kZq zOHAxsgr#@q`YBp77c$MdVFGrzJn86LS8~mU2hstgtufNl*u#$7haX( zpSD@a{g7LyOF!QDV?D1=_*CR_jXPii+N_Ok@qos^NjjON7M;x&KBSkvYpXRMZqT~+ z+m)Zx{Io_bJ|mvp-@+rUKCqm08YY|l#J31O!aA&Z!Iqx==8^Ru*s8`bWR_pQeeJE; zbQLB4MAj#Lc3&)b22L#+R1%*a2xlc0*G7-0i@ne6+R1danke)4+RHIhr1p6(xLG+v zb7R^nDT5d1cy~HwLtC?&-%h5pS@qJKaQ)e0 zWQ$>C@fZ+@yAGYN0&TK;4BHlU*BUKh#DfwRcnI6=J+G=56m2nO)KNHMdIHK(mg>J> zMeyIld#7|8Qfv{kd4I52*M=tQSTgz~wSrewe;l-Mf+z9+Ki`S}!SjD54L-XuI&e-r zJO-@>vZ$uLuEiPMH%8Nn1g@$}*mH6D?7H9SWM+p5ww+mFefhjyyyoPv?dL;Qe#kN1j+m5K9-pE1oPb>05*;)EOUKIe zx<%+p?&{|X5uU?O#ao1wByaL^)X==cln>!Mq_4t4iL3n#r^wJwQINz4&E;{GGN*wb zW}1lxPx`q$rAG-oa>48T|JhAz1>6}!Us?MVhpgb$|JL);QsQcUwv_gQyw6;*@%(A# z4~TvMZzr>3ESj3{EvfPALI7x_x%Tn?^XHk}!3v!Qkw;>{d{sa$v=@8k^}VkS7$aB< zYLOSUpFDMnr^<~iIR*3NLy#`eu%ginQqK zyxJ4XqsY;oqfs#5U;Q+AH87oE#>n8X>wC8^h}=+cm$NnZiq`yzS8*d;OO?jUr-^E+ z6Jk`pNUt1wkQ5VCN_2w&A`87B62>$Ml}FSPuc+dH)_`e$Y?+glSCZ=JBd0 zmTSxo%HI~}|NMJtNqMoWbL)6#$Qq+%STx9fvV>D&r4q_EpAXq0;eXXYYWIvA64-_w z9*?T6N2$KwQnUYh!JdwbEEAEf{80tca|M>47KIh;`Q-a#cx>N^CR;5ql?tenxe4H> zG`08KTieM-lRYyd0``_k_^TUU?!vn;0i&qjIJpgJP*74nyjZU6?33n);%Z6$25p## z(kfS>=b|U!?btZDwuD^kJcfLrECc>Ly3ufF|;nxb4a-LASdsc z-bFpB0HS>Jvs#-o<@mZy)mNwcjJ^KiY>fHCcD+r}M@(aCJ1H|sk1zeKF7%huB%F1R zRDzeoty;^KqoqbVj~})8xXz}`t6rOiElSF(X%TALIyJItJ-rzZ6HR3$aX6uDmYVXf zT0`7b9Symj2C34P^V3X?3SLC;smfdTNuD#1k(F)V{y=yB#EC}ZuE5tZ;^;&f{AH^k zQ=l|jstfbV04{`-Iv3ROWShnE@EEf3(oJm7EdISTBE`L^HJ zqJ3NisgN|q*`ArEC|I+80p;^yVL799`pQI<7dgr@j06z(658wZ+wg` z*JJ2oHZS@><`oa=b9wvEPfIMHtD1XczZoq*m!4MA-tuVp(|E+2jefb$wqyEb5q_`h zA%UIkqn8@xqtW9U)DEqJjo{BonWSwu`AS<`Md{|6PWn7OhA2!Z&BuH~awRy=l{+2Z z=t?BkV|P0(Dyx(1)^w0tiGnS?b-_XMmBWTDO00J|*s5(&a^^{=nxC>_P@_$SZr29E z&OGKjxJrd>we$XDL(9OF=m+b~1CHFeRXnC-+J?W){luYDMi^L@6nB1c>btRb+KsK* z&isAO^7vthh2*N<()w`c?jgwl1G^88EA`)8j!pTu=3zPcu_3RQnmSb0TyW}JaFLV^ zJ>-Y%ap95Amw&u7@u0#)LpzCyqjK7gZ9cd^`_cM><=eM!AEA9{DvI=GZd*y3iyp1( z4itT7Qvc!6U<0Ams-v(?a!fk|zF7XI3itluBQ&I_-o|?9< zd38ncy%F1A2ogqKn{uRfJZ1bP-WTAf&@<;9JN(4CQEy`lLUP(B8nxvs6~Tv%0K5sJ z0~F_dG5lFiax!@DcN>|89}=)K)$$ugc?V)IiMnZ;2oWa5cR=^Eb{r?9x}|gG`mIx^ z_AE-=;CEgB@9I7CXO?TdAs)`B+T80?c|M5nmVR4XRlVOV=e<4WY(b;X=qdNic{DI# zIa$NkU%S%!W)=8mD4sNT1#R<(&Npz7cDDZ=^aW$JtE?RYay?#&0Jld zv}!$|MJ(Le@R*xC(Lm;ZXlA}hDhw0OZ|-!7fBwKUMt4P!CtQw{b009lFl>I0NGM|) z#&;x8Z{ZBr$E8}?<~4P;k}2+8PUD^OZ~{sheua^S>KMa-$mA$b6yb_gwMV@yTXjXw)@C5uZ}x9imE=D{onsRZQwdfdoZB>+BdPIYtO zqNK7H&JodnY~p={W#Tf#j7X~6DG<=2_@!a}G@-1H zn?syU*6q%)L`0=xAQV0yvfAgy-tG75?%cYX>)}DG{CLR$&HV-ppc&>mwyllv{b(_= z;)i^t_G-JYQW8=4GXP$%hKgysN=cMsSo0l zp7mb@lWXV4muB$1ekJYfnig`9J6()ACvX1zMPh!0UE$WaZ4&+x#?!m}#MKZVDS8Uy zynVYsp&#D1Kgy5HugR?cZLG<-&t`26ynADE9S+`&`xcs+Ml3J)lu64~GZLis)NlIH z?51gqaJ9ALnd(qi{}no#Z_Lk!s7v7**uRz}l*QiZV5X03e_=j@-B&0iv_kJVtr=^? zJ{XlpAKSM6eS=rb^T_CY$OXy9?}O!S&x!j6L{GS6ex_%Uv(#kIVN#}&lRZ<;ZHLug zClxSVwk|P-EKu`PkI+|sxtJofBJ(D5P%K>f{A7_u6PXbX>47)+b7D*m*M>X5{sA95 zEv`1vGjoaF^T3e3C`WHih8jOKC*YF2Z`}cnmQEL})sn<>t5Kfnzx9R2tv0{hu^8OJ z^Ft>Z^I~pLMGp~JVOfOJK}VgZpEEj_TOGk|%T-brmNhtjWxi>BxYBad`YA?s75eU@ zm5=W;xgHj6nA0V}!MKCv2ja95@y^qyh`yC$bVY2w>Y)9 z$wg6=hc8WDzruuZQgYd&q-OudKz_6i=`3EDJ|b$i81%ouI+oi`{uwo4Zh4yumP5-S zGy&>)r|gnrmwtM}6>&9eoU!^-&1zn?YCDubp7x&i$*Fp}aoD1tX82TX<}WaNACDhP zc%_PffXvHu*rNOJ_gZ6-;UqlSGjm5iZ+XYS{WQBIx9;kxm^+_h??}hSJLlB2Y>^6B zr}4(TM!r2>`LKAg04Xw27^0HW6EGVPAQx`hF!?C-#>0(5HUiP`+O4l%>LtAFbhr)Z z14}>G-0RZO0IW^EGu}K~XWFX^Bp-j|dej&7AN>9hJRv>dthgE#^pkI~)9Tno$L_CkTZwU#@o&9d0K`#p9h!WD@*=dR`HE-;gvIO?=`_T1uF z-qOND#f4Azv;Oe!$p)%k6;)50f?K+l;Cq9^yEY~*B3SWO3XRYn!iUPWrN?&4FR$TJGP6cPoH8o?p@+mYq|T)o!xVT(9ftKKBs>V zzudwPKlw5@0VH4GlIi8-IME{Lm{!ZHD0ShiRR%0^vkosxS{ z4Q5_6;LFE%5S~3ysB8I zA0v~NAA+ECilZ?aC-c+A5xwEUr^ zc!&)~UNP8MtJ19ncz@v@K%;?ahkvbZg15xm6NB7B#5GdByBni;!IY{xfd!{Q4F(L1CpG_ zoTX9-Us%U{$5&=bljTQOvtqX*`b8iGwqBs1LX0)TCt0i^jt*-Kb}{6!AMjy$Efp*U z^X`EkCf!3uz(|j~zW02;_rqd)40*NxW9dH)QCZUC2EZD;r~gyZ;?Oh~_=gJ~1#5+< z7FrS4%nRC>11c*kYoI1?UC}ShpIKp7Dj*t(meL&kJpvY4MyzFy>7|`n*O%5&P#NUu(NWnKeS!(@ z7|GP~rk=DWG!$i^9%Kec?fe*6S*Z|)7R`SXkPlm5v;~JT(fVsuUB{k_3bR8jk~J^J zSKjip?QPjDgbYr&WE5s-@^m=iU1Y_d^)rv zf^{t8Shc{;DSaMtKbUK#AL*xtl0OPw_&9!Eogr=Kc09OS@GAIy`?|x!Af7VLRNM+aAT7)^57cpj5_*9^CT?6FEIDC zAWwwAX8ksm@lI#FO3@TNyf z{YMjg0q=j|H3EA$^m*x>|Hbnb$CwQYT$B} zWk655ttjf~zYEE52}l?`Jm_b_p)L9hfXhT~q0<;!2s94aKP-zXhGd;wu5nFQ@{8LT z97pC*rCNL@RCEl0sQ6}bDcDT{ZpmG>lDzOI7XCDUU9}>ay{9uX8g3QioJfZO9^=8W z!|2R1z5=(|F-VuFKdU~*3>3yY1lSP$I_UBBVo8ewK%tD!WX)Ow3W*7xXf=CJtHMoK=_`l3nnL&-@_NQD}O0s+(@mM-ADN2&! zq0JGF80GI-rUUNM6&BQM8z21I+yO{o+mH~*c1i#tyz1%H53DOF0zMJBDqVhPY< zbLWQ~I(lL7K%!Q~Qzr{)MOEbt{LDJ)`D(zRDHQ_v7ZM^N*OgTD6DM^Zs?NZnI0$>o zZs1+lkVRuMX!Gg7v%lS-2%_NZA`cU>_0~jqWO{T4>@Dk=`CTXwxkG?&PtoZ?saun^ zy!%Q=#CUGR$ZP60H{y7t>TWtpy22FTh*HwwMNEm$e}N@@Yo;*+PPeVL#+m z81jVKOT79VjJT&Kk@J~LJ1TS;Q1j#QxdE(eoHBkqQ`%|_jvCU{2bVm%rjXK zA3@~=p3ssTq;Jy$0U(M&B+YmqW_-^;_sSLj$wIBQZ2#C*#+Z>~+jtNbKL8w3-$Fqo zvcVpxir}8r&ws?QDyMr0r2J2Q3~2@#VU%=wV60UlkPDtKi%cFq*maM(F--1<@%`m= zU76^;`^ZX>Mn**Vm$=RXae0ne< zD&0iu8_yY31@8zR+yN)O3u#2D(g90B#|Zu~^rq5t^yuFXygvm$*}atX@e;H*3ZCZu zADl)hoNGZm8OM&r_wj;qdfnu)dcSJjYzq~mb;O6Mz)0`7hw2h?`8FVn8feqh!}BQE zVK)<4p)!i?>}TRUeZxu*CHoZr(VKCzy>{E`jbuNo0w<3*@v_CBmYDP1NRgk2p-cYf zF_+Hsk8jK0rl?!(_&ea_;+hqm2bm?3-ijqvRtCBXKuHsh#i@Uz!GYQE!u}1b-p8Q| zc?H2qiOYQR2X4Pm6RO0Q5Yw4mi9Kj+In?&f^w}|Xb~Fx)-25A)k;CV;_c#? znjm+@I)}lp@_(npiOe=~Y09jvt$ie)m2ejYltvu1|HS%?{D=3OsUs%Ix_}PwB{RX7 z(h=0x8RUSrqbjOnr}DQg`ufx7U3M^KD(8QkFt@eKh3Md1KttjU;+9pY)JIDXC#|P6 z&D`|@HwSf$&NIRz3r8}p<5%A}>`J}02QkqSWd;X*S5wfF_}+?2<0oK+_8YkOd|rZ% zL$^!q*UxN3KbwwL++qausA}{=AN%q;{h6{~B$8dgcsS^Cl-4kXQ)GU4DKUyhQ9&oE zXX~!4{7r7%dKnQ~G@S|R7LNi+hc0@M(tL>gideOLiM_7pd>X1XExSN*`LlVzTJ-wO z{9s#;YRUCQO>{-rILQY^Jw*+YJLfT0WCXg(uQVsXjxvo7%vCEfDff=E`ug0|GnTnb z#$Iq|CK3c!A}btQjzLL+01n&Xo-q>wWrZW7a`Ke9L3IviI ztfx#J2!eVVh0(sS+qtZdP?Xm91$T_jEolTt&)OjFg)RefA*6dLY2R`_B#V-(Z4DFn z!M65pE@=-{9jSnp)dH@Pr{1x-iV8ig1y-d8;xoZwCychL;!~HXgZ+Z5PldWP=MQw2 z9&7^Fq$J4NdgsNxH1%`qtqpExK3mh}NpGCruW$*{9vCkKOFbn=%J~zu zbJA@~=x)xvmXhMSxh8Olnd<#I!^V@hG;e0br+$t^p{W%*emS7mop}>QxVv^U0%H{N zt0SmUuVIWMIwmzs?9nfswGNg0D@FXHC@#`8hvF?ITI zs39`}6}%%|eMmqzqIa$%0FBc*sVEzvg7(O`R}vpSjEH-i;(vg8j`S862X}X+^*IMz z&D|Ja_&sR z7xCR~t_h8f_9ddHCJ>Gs%vdi3THK#dQfxy7yF^k&CD9SOF=8{(5CD#GmH3Mg7sLB# zrtm#7PJTWWxT_c%%53UE*{~@Zr zjap_ygl*M6XQ+r?(k198*t38C^cPJBUAWV^VE6HUIBu0>96C;RN%S#ayZ42)zlS9qA*hhzHC9H|F8_YBX7wy%ibc z-#~0B(Mzkggnw4-F??H8rq2f9YaP*I2A*j!s8 zhT0)_(4A4Os9Q%#Z^{#DB_=LgPm&OPIq^FU{c^7rd(TS*Fn&LM`BH6L^8mD?xlUeW zX#siyE6`EV(9R9$M)`edH`$rG=rl!iK%`|7tzu^-`h!$=j@`F^|GKyGt4nrWI8X)p zC|}}-Y9g8sF!ep--Y1g2nQgJTFg$K&Yb5^G-|k2;wHwh6pazJNbWPf+*6|(xzci^&lGxsxQ7zok>=pjLoEm=3i2JK`|gg z?7(QZHMj0l^UGi?jP=KjAUFdB$bFhMSB1XXli|^+^3+2rWfKA-{dzgHK_zNDAp!!O zTnc^Cg`fQ=U!Ez}6Gp0}S(wE>9l%2-ovKBD|8CYoDm8=?P5=s(^9n!|?QM2?+ltfl zjnG^}9a_If_kbNMgF4l*+eO>D%1~pE2r>{fYV~NuZ6fwAL`|k>v0ZE33nCbFgZ%m9 zgb}VjFs2#kUl68-X`+=xA-idLl5GrH4}5;l-szqrWsmNO9r?5c(_a?FcAseA*WsHe zqmTqn%YPj=(CHc*i!!?a-TeoGTFA<;M1L(8z3tw_8vc8fE4nGh=|8ImV;>T?nJB7f z8c0B%GNvREZ7pe8GwjQRh*<H$ULi0)D=BIU}3_HEgY0K1i>QK@Gz|H8HC;(fWYYhyzajlIv_*Nzn zF$G$94nI>_a^Th74(KWoG?<=%={ zalpVggE$#o(D&hmww(i;i_6jdcD#NjY~%!vsC)PAr~+C?>g^ffQieTQMGXiSERz;6 z?PlO2KE9&HWHp-9Tzy2r#d5*b8}vL(yB@I^!xW~JcKFR%FX3ws!nKue{+v(+I(jc+ zXHNM?TQVOp<)a+*!gJB7JLBmR>VOr2a9CkOa?r_+s#-H8PTG5ogD3YEgnX96v>(NB z=xkXD7JhC)Q19XBmbeP1x*di=)bDXbBB|^tK4pd$7*C?yczFI^0k~-=5Ro<}E@Z*^ zsDeRmu<1n?V{zR}>b9T{j=WS2dQmUphloKlRt7D?Oia&?+m&}dq4uihRhVmZ>!b?l zE#s{*|EDlYZL|5YuT~12ot^LLdNEib3w&);skbf7H@T;+l&13dQlG^}ED9s};LFLJ z>kE33kBp`RLCq4kuF^`KwqiI9w3Dd~Pl!&=EO$|SgcswSgI77WG?7fi4*dY4PI>}u z{>!RLaN!A2H(z%e_&k_he%=;L?M6yjiP@)V~C zE^5&KUT0tBt;u}34Y3EbZNt@1BH{4RcMW?2ziv|| z*iNLyRRS*6*`o7tTy|pr^<*XE-cVT2vxdC3g~#FJP7s>Tq>u?#AmAeL-%w5HiyBsy zT-jskf__|Ic$J6KYZ5>!a4jZg330iNRSQ8ASsbT_x>HY8_7VRKIB`>-)m)_hxF+<@ z&b7SXltmsg7mnRN14M&XJi%cIS zE}V(G25h?vt7tQ$MB_Dg^wHm3swM<^U{H{yj!#9-PegSzrgJs1dARzxQx7*WlYmnp zc_s(^D`eTI%a>+)n4zimqg4eq$#fwh3T|(RSYyvxeYka#I4ZjyA3}hcMJnmnN-mVz zahI_~vau4knG2)w-gYCbk4tY$kT3CbFT$Ew8W*0(rO!#rph(zgEoh5Wdl_DWIW1Ob`Au%-v27iSQWh=)f4 zxB;A3Z-VQCzTNiifr(eJgJ!+U52~!|fYU<+0zTCAUn~cfE?;UAEpDP7=wU|Abakqi z{SY+MBY4^zrdAHKO`luwo+1#inpKF#b8Osq4lNfdMQAQj75IUv5tle$aq5^#L(>6t zL82CPaJ@CPRxTLp<2rLNYNfZHDIIy+(kUo8KsrMix$UgjkMWg? z#1=%*7!s?ka`BOoi=@)Jo4+&WqLWkjG&8#)+6*0@FK!#y)J4WO#2rLURe2jYX3SE~ z{i>?Ww5@|cE^YlYbK_JD7qGN`g2Prs%Ylaby=^!7y7k2S04A<=7|BPngg}PrO9q?D zd}tl&26n9|1=)z~!PC4jmw!We|C#)W?6--6Lt(fPqcpgGjFzI|e0Pf-iK>D+z_)BG z?lW(UtZ&A!9?|E{9cVOuUiWgczQvM^g>8P|<>jEoIjP?l+ z<+#Z6#{E(t9JB<{lExGAQ_SG|rzmb)oZ4YZlYJVBR|FtLMJc`djpyv*S`hCDe-eb3 zgHQC-4HO-FO#zdh74AtEJk9dDi_qK&=)WFuxS}G5Aqe{J(r$2h#(*cy$X6en6w#cG zXlB~oaW(Zy% z#!6ix;sa$6_uV$?k3f;!=T+GW&Qi%=(@rjf>Gy?0?mWXd)uogDBp$34tqE!HTPtfA zqeLWuX3(=IWK-U2kTtQiAxdK--{|o0>F4gf0`(anD?|cUa@*kOI$bQTtjgJltF%PK z5>nZ65aGEI2NmkKBh+p}&k_Ek7*Q-Zi$VXh-)vriSx^cs8&F{S5;TQX#(kfSxfgAo zoPZB7HuoM$XP~01ldl=^1F)am!<3R%$4wVv!NK1FE(gs8juwDch<~W`J}gk1lh0|X z?(RQ$jyoNkrfPTCWawQLZkVx z?PrL$gXTTuNiY$($y>vO&_y8^-RvJHcJ%Vpfl^^fM@^ca`1_{a4|vz8x)u4ZCH6&D z)H$2?NS>;_fR~<(&81d#uyK@eUjqG@Zrt%;N|OJYaYeKisKrR}8Gmo_ z2%Dj%At-Rv>`L6w)9<{RqcmE+D?DB-pL(=oKqBbj@IJ9F8*WWm^!_pjt~apQCQ;x` z1ZoqE2e6|jZM9E(YtsA3N3F!maO1i)&?;|?tjL1wF|H>~vsyjbcq2R>=5CU^1#(Oc zqN#v>T_#yKmM-BFWljtd_LwUxOS&C8V1T7SGT+N;lpbCk5ry8ZUIpZZs5Wi0Es8`? z{mCTC(nFU4cd)A7F@>rmGyRVnp5kYV(Nf7i{RvUqXE`@a*~^%;%|M6@CIf;JzXHW& zZkLkLNe@inDtHiyp_@ZXxZCR85{}5S*EH)795)SVv--eYz<~|xQfMOt*tct*#`zbG zD_$iC+k_Go!|DbC5#fcegcBZb5H2pMyr<*&FG zQB_J?lUO&_Ot?B3gnZw6-w&}e&oE_h)w z5$lSGcD>{z%}9oZTZzFtPLe--Tn?0hVkxZ!1rZ1;Q2m6|Txl60_lD}~+X#V^3j#^3 zMnwyqQs`GoO?o9iBb+{;DX(L131iBfl_pJn->8NvFC?lzAJA06!o-4kfgA}6)V{br zetu)U8KzLJJ@*C#4BjAvwB5{U(;4MR{2GLFa$kZ2oD0vW-YVjz`;yN%{rb;eD7YqJ zn-+Vowl!>9N?36WNJMhDK#VeoZ78oZzpp!==zF+5jqdIwM#OML)&o=~ht3qIBAhXU zx~_|DgiH4!roI_t^L#<1lC;%};Mtk^ou03=ck!3Po~m_^9*J;?DMC@E1T#TVy3%6Bbf$O;H+?{sD&8y8Yrx5fpe`1X>XT z{`T=-L{j__Br`h~AaNGu0BF4NPc&RE=;&n(qS)?RqYt1nnnPg?81_aO7L^1L8r0!J zxP7#O8>ot=$HQjrGTsjY8mqh&#fKM+EPqLhC@~W+2fUyO zE4P})f*g2M%&UsZ5=)boI~+0R;je%F;AN!PzQbXxO7_Y21uW@TH}-yTo^0~slOw;7 zMfyuevVrPvg=cZ{^#Fbutb_vNOfccgMBE44NlD1+&Qvw7?(BiBbR^_5*@7IPEwFDm)g!4i$)qYQJnG~O zyO&q;9VucU8YSXolj@Q!a|0>z&#-@nz8quF-4Ng##d)xS#g0iARo>CMdFA)l&P4mS zUN_G@B4&57H~-^u@9xD<^5u03n|lA1m3(FX&hJ^T=k$)@tpnYDrF#phb}e|+3yeTM zymN-wLW<9c1PFjqf}YCB>95FVfk)^ep#K`9SA@;G0waU(>hZiOjCAD0AiT>+>#a*` zTWLoqDgmfF+heZ~DGqR%7v_}|wo<4b;7S-4S*{~VAdB#YEXxu+ge z#)K6_69y0)fiib>qvIxs2HYRgv_!$SLvNXCG~f;)w*@0cJm^B?FFd{|#& z=Q*Mt@zg}#e6EOcq~iUnu0v*pvFx;Af_whpfr!lvfPSM>Aa`cETDBOUI)Lt^acyrqP&?~Yn z0jaIdt+vswdXHV~wlCj^qO$A1)Yx3!S52iYxA*2qaZZ8#W)V>ijj0Vpgu8~=2;ts- znOhTOoTi8p3kvA{T`6q7=-&IO&$Jgl-E`0xnb7zcAHd$h#g1eriQt+5feZxWN#1}b zDIoI=#4y|YpCb(Z~g_hF|IxfG!*lwhN*nPC>jb>?S*EBuSXIzMqMl=`>s?^#8( zdJL5lk(!f5a*}MvkgSv7|8)sH7xc?hw2ncc)d|z47-7`CrqTOzL8U%Jpg|jjb?t)j zZ=z0U@0H(`vhJAajI3orv%Tz%xDP?SVBa|t)nWSkSxW_FM*bhE`-tD{q*_uf+>C)4 zOe|*B`6i2eI0uNfD6#@I?5HLr8?zS0m;Fp+nKQuV>_vNR3r5C&j%=?*z~@WMg;X7Z z*{ohLe?lp}1m?a3klXdy1#vF~!;Yr;k>4b>{|iBcs9NU|n>>LuzM?&~>nV5EZTNoDPHl^*tJK}mfuhE154`{Y&fqG{DQ-0=9O?Hsr6>(4DwD*l)auz zUh8xhrkX=zA%2EwK&HAk<0 zTz>J7!RI?g{-wJ0%I4a8{XC?hMiBQvKrsslo^}9JHkZ6wf=?Jp#UND+umtLH!sS5= zZ&jcnn@I%c-_3{zDP>+#0~JH0+B(4l=n0+-H?#YXG2LhUM8f`x9;r1u+Sw$*!r;-} zH*3Opp!_uq&P7|+i;to31mH1~#xdAV6f=c8rr;;quzy5)PlI_pp$}G3md)0-B8q;fHKQ;2NJ0#eOL?e=x9>06gLXW+p7znS^TtQJQ+7e~Cy#DVW!fxF3 z-@*GuTC%^Ox9Fe3RW-lsyviEDc@(Kqp<6UlkIyv}v~^^~Qm4gHPo31(uPaBDl$ofR#p3D!NEQ_Feh?J# zvbRVL{xaq`N@@R{n#QdAa3jznK}D>i=yTnD_2tek`JemxnpWvN*FWw5*V1V@*LR(+ zXc1CWjM-ymNH_o;6GDq{ge8Q`h_%Uhv8dzt%MH zOHQo)k0%QX+t|r^CW%*1D%w3MH)pPk#d19F`p6uS#^LS`EBgTQS5M4hKB;tB)~wNG zHn=igxqi9Q6W<`k6WCeBZ|&^`;Nu}#=XsAf%PV9c%oc7{v8Ra+l=Y{{2qGGp4Hc&Tx|D){R&B@7v zzY?bW#OD{HXJ$o%Y-?64Q&q!%N8nGOt@`@GzOd`w_DRCRwGB%Ly_7!V&)a;!d*IoG z;~5zl2~2<1z>|(5JD7U;foHz{?a`7ynHK!7pC9;p7U!SNY~Xd2vx}^KIYhg#6=u(! zn^iuC^=SCN|NU=fRc7UvB--3e=AHIypRM=)OymE-I<)YCVBKC%*3fnTZBCb^X2C6t zO=IK|By#gUzc1k6aDvCm+R0B}2}f;z5X9QRJa_7*L&CsPUr-TUee=gU-H8 z1K!)sT)j|8aIfABtG)qR+ zZyE&N#%s&?`m}uhc7IzW%eV8hN|zq{defw*{Kw7C99QsT?>7sEo4;Yh1~0*G;J@+I z@qQV@Gi7h=8p_@aR_4(0suI)>4m{Yag!=E=z_UYs`+qM!hen<_al*=;lvewgsK|kD zOITm{bFV-Z4w4YI{_-6?Kl;}vYiQhT*w*^Yra_8dGX2N~tjcE7#*Z0u*j}ywKp+15 zn>^jDMk~cYXwW7{!SE|698*)%7dJPF>P71C>;-f1U`zkfP;Ao7xgRy-a}Jal^4boR zqQv?gUCY0pnAL3HPAC#BUiGVDgB0H=J+cuK6Dwox{Y8%xCw}o_xZY0P#CDGko6}ux z$deGIsNBYWA?m-Lp}!nzoYntrLtp9tOYYdWYQFzP=9jm^{i<`w3kmZK2kai2Uax&C zb=bU%1UK+zb7H^tgaO>rzlDYzbl{I!oI&cA|1?{F(&Wi=)kcjObGC4wt-T!Y>4YPE ztE4n@(uKQ=KN?rL*^b^5$*dJ`pDRWE(!UPYn?J{@@Rk=JKUIUz?m=On-*upZ63drs zflA%Vo^XBUG0XQtjD4!Jx_*Vk%-|3;;0|9s#2kHb1; z_U$cWJ3oGMX;t~qW3m;zzLFBh5mz=19EkVZp{~K|+IiN$0o zSdTOn$_xeHR+e|w_j19XpE(7`d@pIfjQ*0(b`t5f{0h9$6?$cIQ{xdmOa7HZYTiYKP!*NJX=CviU4FW35hxfnC zI^-P;pND;sapL%vZQ#|{m)8yQoDLm7;o)%#UlF9}I#EdI4da9~Ow|eZmlhN=^ndix z@ct#nbN0eKH{|HCV@b(~{MpIdv+8brwY2=tKAP~AWF5+fGs^$ko%gnv^VMPhS3x8E zICDU%dCyx;`Nr`Pe85qC(B>07IrRU3@ONHc{t5RiFi?1qD`gVcv^GIL^5aby`35Q8 zs?ORrAjxu}P92X=7pTBrE#` z{3{lRFZ8ur&SEPpN*f4toWJ|^KX~b<{=otytRFsNL=xPtL)rW8*j_g5MLr;@N9vLPWR_8WBQ5{-HPn!sL#%>lo9osf_av$k)yaC}AW2Xx|F$8T;r zj&rCbLMOawF`PT$5$rOutw)a@o%+yaJigsN*TGfbI6KcF?vBy+WPJ9EA~EDK6t zMM)KN)KkAAZ@U7wZ^izi4NSby-`=ORtnEp$S4s;UnoXCA+WKWD@nrwDOBmkxug<{l zrMgD=+c83$pwV2@m$oJbcyA@o?X|-x!@-=lagfV9%hwYU0cAI?fUXzr#HjHDp`(4W zd7taXXY2BBIVFn4r!HYVnv+sf`WqbId5iWI39EMKF;auIZ;k4 zOFrt$Y+*sj${d(}wC4;ssjrQcp3Y$>=K-LdG;?ND6PFW?p?5gb_W4himF?fEsppbr3`^PV0`v(8^UX>araue$hhB%)Nbu>Nht9*$K83|N9zPgACT& zUrXZ;${zC-k-LhSf^f^qj;zc-^uGxE4yY=xZEKA68by;>s49w4P*J*ofF%~H3Ia+M z5%2&4B1O6tk_f1X^dbU>UKEfHDiI4(T%{b$SUU3WJ)I3YWGH!B0GVs75{W+VKTosU*zZ$A-t`}>}_EK`De zZoS)a+gx5APxjL*^A~%yU%$TK|M}c^|3rE=Hk&4xo};?E&pm(sya(^>b%cBoEP7Fx zb}FUk_QHpE3U|JoeRxvL?dv63T)sBgdP9fym_xwjJ(oW(n|Odbk-3T;rmeok?1|kM zKYBfQ@PKG7G!hytUAj~a`HR>8=FOWdm>-xhgB%KzwzF|)khJ~q@+YG&a1CH>a<+?$ z*VwY_=Gm9EZ07s1aAsU*hhFXpv|=3h<`VyRg8TRT-}le^zr4q2sI>rDzY>>x@O&%_ zkvxjq4=Z8U7}#Y6z};BjoW&oH>(I>VQo`~aI{kP5$d}Pzfih@<1KZ&y&h6P{zkJ(J z8xpZMGVS^_A&nlv-Kb(^I%%h`ph^OgP+_4@EIzexN)b9^B`zjs!rPPUEGc65>D@k~ zFX)Z<_9giEo{E)ASvz#M#40IQRVg%P$x04DkOE^Z30vg!do5wXW>W z`B~hs!p^dg&`B9wJLno}n!Q#p$L2Z5Bs=RbS@s~}#$IR!rQ1;UFx*~UXvfL*ekMh9Vur%X_eW>Gn;tB`PYl~L| zmkjx(vEcI8#p|iY9i2gask}e^6uES@VBG#2OR-C8c#Dp%-Kqa(;J|ka!R;+4i2nK; zK{dZF{tqC2(qj2+>K9Z5*zFDbScz`s;vGBI+|0G@d4N^z z!TqCJCrDX5Wf#5dP|px4G3myDHsf?&OF?T-JLaezr*K3Z?ypH zOIi5cESkAhgRiLTS_WSMM{$AEnD4Dyw|@BHhru}4M?-4S$_TJN;Bw*6sT)oA9-qmn;sbtH5XvSaA9qn7N$)4`-ac5J`jqY{4jg zQ7-VQUJ83s@(cXE=nVaJhe4t4^!UKVoY2_VSl9w$Zr@&sF}OxfxY|#&Wfo0Q-i9G% z%m}wBg&RMvtwm+-=jRuVP1XmWfD^2^2zxmD8jsGt;%Uh8iVV}swS9}#I|2VzNb5)Z zJP&jA4sEf+y$h=@gtzNY;MQ6~vyQ(tJwCshmv@H(e31Syg2q7YEpo#3IZN*RaCsRb z%Bt@*-M1Qt=K~+!z6NaCy5&}Qw+OzQ3=G<~pC7`KE2^qWlx#T~o&<}qj9x8^rS3f5 ze;lq@%LIG4?KUNeAf+IlR-u_bJ=2@cv{<-$#dGTknDyk9J8;9Bg98TPthkjiCs^b> zVX0T(q_J@6s+XB{ZZJtGci^J=YTSBmHCg|mT5dBx|L*E8#Un?4FS)fog4^?N9x=Q+ z*@l~VAtER>bG-i-2=Zh{y<>kU!v!nh1g5gZc%Gx;rAyf!%Q$^|L=cZOkVrIA^<|@k ztu^GjW6z#F z88)4YDq+$Z?d|Qrx%DT*yrlJPG&cde$ZT@`xrn=8_&q z!}rAFhWHPS=0FB&a76h1suIi}QuZr09Io0^efsoiPT^4`mA8}?VH)Vh@sqZGce#i2 zg?BWQ`LXfBcWZC9*8juEb+~8$+xB}oFF5hO(#_`&B+fU+85Vm`_-MDsjke5}FOP)n zaa4n`Fc!YkctD4+-4`}CiErMrrK^5+hkpJ^m>YN9u(E^pshxIupT9xrix-DsS%2{L>(>)#RbIGY0Ywhg02O8B zYJjf?z+`c_s9f;)gX@X`Lg%%azy0wum++bEIE%69dOVH~R>w8M>6vq!#X@kQJ2&~* z>D#qGTS&kqwP@D&`I$Qb0RgbXM4|npq9sPb#$}D)@Sclnr+owgvBMG#11QAcb81rb zWM*(ca;@913TmZ<_D$PjD*+zAsgA7jK9O)j0hfwh4R`7ln4FqQzyZ#&Z~i;$efyiW z)qfQk&6&ujVX70Oqt(!@PedQNv)#)4$>n+42>U8^?|XXU(a?z2l5n*nM~?UmMBy26 zeP|-0wJX|kr0x3y@v^?zV#{`G-|Ces-#vE45~%rK6~Y%yKt}F`F<{f~_5Zz+|6L1O*NuGobUPv<0>D9;b@%Sw z)m&V_5r^V&kE>*JIr3;auPx`wl^Z}8A%}M3)9wZYX!+MF9XV17!_unNt7HE9>o*** z(z-e=L>V>QKUZ^q-~4+5>LrE+0E`J(n=F?%p*L2VY`*-Ym4G)Kh`kZ+v$S*b^Yc&K z-+M(TW%-=xcT9WC!XoMCpMU08kdu?cZ}gjmN45{WD(Y_^9Bk2JDutTPb(zS2c6@~s zn1SG)te2;W^quK@_b#vhAAkN?<-;qJaiQZ-R!xv_b_Z;WwmLi5M*LxG@40h$-P@OU zHl=5_0=Q*5jhPj|l-}Evl^X8&k<(!a*mNQZ=~A!IMxg;#&K-~6W z=7HVG30zV5wza_7dlJca4uu5$g52KPvG4tWLLKOwqQj;~dm!?J;XcRp4 z$^4V7UN5;Y2IUm}1TUTwLGm3d!nG9EEdV0R_%)ZVvjK;GH2ns5yHA*#QUog72(?B0vKq9<2e|D-*lK0Y& z@8)2~QQCCa=L9CQh~ZN0rggu6=VQO^+b3Z@MWO#h|3(Z5SoQPI8$-%Wf(so#o8Uku zpyKGQjJ!r2F+9#?adB}WOJkLkl~3RfQNO^zbHVG@uTTB*<W-8&_S0P^54K_=YSO`qv5MwqtSCGM zS$1^4jRM@dyJn}p#nPo0Hp_mg(G!_12Mf0$i&7rJHr{7KK-#I7FS8A_6&YZ+N!~cy zfTfGV736VXD$f1&`%eqbgG~&<{3JE{6XM3NSMAWvt`xJvZ^LrmOVCP9p_qn&P-=i~ z{0ew#b=a`($-BED4Zb|{@QCj^r*6JY{US1jUq_UZ5llTw&o^fuM?Xsq-gKykHIu?T zhIB+B49UDvTCq7}MV>`-_CqxB%!_T3zVQA!m$=!7jJwxtaS8Gt`2_`I=nDM!a{3Y8 zFgApWubAtUEw8N83AANLu#C~vSA)=xSY=)ntstnEn;~q4Diaw!qF7oYM1?*CmaBNI zXCeTVkU2%-j7!631^^ej%EQzrM>=aDWXr-{r5Ye_+kM+(Ik$bWtJketbyOXLb=k(x z|C^fji=VOWs&L-)Qr7bFCe!=^k0yE7sSJ>%yw3z&U0gD(+xOoS)Oc-M!NbGTZ=(ad zncknL6@y9yw`w+~o8BU>C|uS#0YE`D>mpJ< z$PTLAG4|muK_IM|xK(R&PkL}jLo7`C=ddS6hKG+60Cc_zw(i_mZ%vj<=)A8HXaZAG zoStFSCIz8w7s}4}^6%S`yR6fhhmdLKu=d}yv_sWr7RBDU8cvTfhFo`)w1Q1|4)KUZCF%EzvchPr{W zL$RwOLJOdb;-#abW4z<%k4|VUe5BV=;1(W(xg7UeT_^NBFCS1C867=Q?8oX4nsCZ{k<-#ZHTgi`zO- z|L_2!nDrF5CVp5YNaVRO8zwxzGYcJo_Yrdik#q>w;DmTr76NMapm?o4yxui>(Q+=1Q~?FB z5!E49h4HS_<5I4&K>A3>3CL`XX@=MMd*HTI2W;o@S2=RTx_jjVj50B+Pm6^4daWx; zl9DkcZ1GIMY97a(RQuyGxH>>VUU1x*4W=vee3PbcHPG;;b0Pu)3e-{RuTKrMOP4IM zzGQ&=&EoHDIxM|h$?Kb}W*0aMtQve({je8DBrzI@$+c*7%W$2xryzsvC^le#p$;{m zVUp40#L`CUz_6VMbUm(6z#q46X`#0?OkYP)4SyGR963v5O^Hak&lUdjZH0P3rD7`P zH3|3NZeNJRux;bUXMy1@rfexm`UQ>k<@fhpt1fVMP^dx_tVuUL$`TDg;Y+*;`@~e1 z%^-4U>J+MQa0vUT*lW)zBap=@YH|)?jy==a*|`tAS}htc{X3@pEMMN{Te@OJ0KfW4 z!OJ0cLqn4g7xkZ)BPqsdr5OHVl2>w}qr_NhRMi*3 z;{BL5|Mp2lMPdHe__)lWON&|L=e5^~<+R0*jhBQb=8)>P?aJjIs zFr8jZ+zB1%xVsuZS6AZAy;%X?h+LGh{?CNKx+NpjZQHu_2%h>laI(sD9fU7x2W3q> zK)L}Ce~9AWz1Ug?l`7o%=fEBPiFL!d0T%*RN9o@7Q&pW#TB-U}4m`3`Mjt~I+F7yk<_X_0z)_Qq4QrhaS7MHTSA>u&QM5RqlaYe>O8z89$$ zy(KplS1gh>5|j$!4wZ5=rn8tZTEHAIkVI)qqh{(8dt!mlMbghKwO*SAhY(z7Nn zu^h1C7|=)^ZVrye?nC8E_zJ@_B*Jfcb4!-5vSmCxS)IX9=asVg4N3Gaur4wrET9Si zaJ~ohsEEc6Q#QP^>5$hk;3KbL@N`^Z8-F`|m{>3NIvL4LV6i!nzU-M{>+r9+i^ns| z!)2R#My@Sdt_F}$lWHK};g*LkwZ>e#M7#=1o?Ejs(>S&oLIx8+bpg=*6&3$X(am{) z?v|4hA!VyH0{20+~R@_WcDE*E>$8?FB>- zWw!L@f8+HVxCEIn9YL1-^a*KEI3I(@cc*0l&g z#)s$PhMm)>6I41t1A}^dc@@b>6SEpM@UkB@KW!}dh_g})7!*mID~9L1)ZB9XpOVyi zQ%2#xVFug80+e0iNR4>TT+qY`AWQ`%Bx((Fz(iqNE1(9d!%fy7y1Go?q+3~7q#n7m zkvR;?`v8?!DFgU(RnSG0Sp-4Rw{@8Q0C=v{HupI_geVQX8G)p9JYHj-Ln@uS0_O>{ zH-W;;;FydNb5Iq)8K>^*gHka#WpGo-JY9|DAUt8b>?-(!(A*tz_*O zVWX0>Qhq`*r~}V)6=66gXaR@8P)sNi64>#{7OT0Gon7K1LWeZ#zLGN~ZvLEQ`T3A2 z{C66pL>}-A=KAsiii3oN=Ebh^8uXoxp{sOv%Pim^4_9;+QWofMO+bFa&je`^Z))EJ ziF(%^!SW!scwMvry6$DgRxF=*fzkp=F&MK*{JE?@-uv8-^H(O;Ez%+Eh;b3{rWj7C zX4h1>{PFW<~7G#Ht;zo2Bh~{m|4esxDjpOhETxg9V*d4F zJ{!u&sO+Va%~1zPqfjJ_LRAqSNY(t|$?ZbdB+`4aSEwRHsq0*9{KA_%7FD_iPTX5h1gk`%# zE?yWr&+hvWTKZ62l}L@ED>(tI(8$e# zX-NVsR6mc5P=f(_+kv8}Owwr|%$B@<TzuBJlk@kpg(h1K3iGuy&b8Z0;k|$;;O>~eXxT20Uw-)oEKrou z3!k9<>rmGwp#L{edL-LzCRf_F`*hl$~ui&*N+2;s0`^)(T<#>UjH8Gn-Cw% z2}oZH%91h*9p&e^+%fWr;0ZA+1ln;t5uOEh>=oHkU*|OZ{vdJDW>wLDly>Q?cl#1a z86Q163a=Y3PJ9{eD6z{gGf@Zqslpp`a{C$z0-UkLxA5lWVsj|K{L?x>=k`D z3aOx{H4#)#SX)~&d2=W3>-;F2{L;~axh|mB^Pn2pu;sVX~-xT!V&4w zRBD5n36{1?fQ+~~Mttua*na*P@so&=lC|iO_eGQ%P@cbpg%g(t?Fsrc%nzE!UOeURl`KQi_l#tsk_DKUA*i3k!@Nj=lbg(3e}x& z&CfyNOC-$9DxB)wh6<+#Em`6_rV&Hp5j5GagpNbzEu8$w9bgy$`Crl-vuR3+f$EWU z!eG!bAjt$|Qi5(MMYaM`{jhN95nqh*GrBGi{ds&`6G`<1Q<)Q>N^P@}{|P|Z%`rFD zWP(dTjCN(tOL6SkgDFhDrH-i6v=G-+4A*1Pj$?)JLn50NjA_b$PPrr_$wL}iL}MGJ$rNy-ds}yXc9CqA%too zSv%8aNwdpjXMjesws^>*9LBLU zcGN>C0&D$b)cGN}3s+2n03Ci`cn9QY(1bB`+_Y0@ch$^?2DT>0SMY9wo| zGiM%9xrd_c^@|r=SuiCQqAeGK&NMYLS5Zk7u$f#I}kvZ*N zGc&zN?H@WijF!5MPE2UQm{~jyO)CSG9xDS2h`_`>p+XOYl{u^Z5&E}T!=so|C*2#i zt;?oAF)^F$S*Hy=1j4eCP8fiPO?0~C`ST$EF%;g|E8fFYTZTEbz6E2*uYdr;0%MVh z&qtm1ETa`iP$P2xLeILPwxXipCSFd?c%;9;+ET@r7$_MlW>m7w=qsApt-pAy4!IP! zVxCI^j$~l@dH-Y}RQ4!Ak7Le(mjRfLv99428pP0rys6N^9_@u0bRvi)9!gR>cv%$MI2* z3j%AfNk#ZTWse&0p9edypum7Gn*1cLYFL>>h!Alp>;m2|cAD6~j)qN_-|$M?z7z|; z*VU6_h~Prxeo>M0)f43r@~N1OzN@ru^I&5vu>+eocm8qu1u|(OV5m^ndVpt&i_mQ- zgaYpx;G^E<&~?q$KbrK1K?u!I)%e?~?R-<`J6V+N{J~pj@!#O>orL3-ms^T2F+e3f zQAN5DGkmsy2pz)RAkTRHId@y%M@$Ipu^;J@oKxt;PH9L$hU8@qL&%d7TX(dpy*(0% zaoexI9wMR#Axqi&4^a=?hbHza@~JCS^$hnpHVwgB!n`pP8Z@6 z)t6icui$(%>hP^vSe3-A-|c1PqlTtF7>=W;OcAq0U{MK})gOeQgOJ4n2qo*0~zFG!@ez7zjY@`vMR!G;M7&7Ohy9ggq1okVrGlc^{ju zZ|zprm#RSgQsG-QuJ1agLHYH7$Fk!%5e(v^#O-=d7=6{0`wYIpF1Pt_TA7SVB~BHx z6QPGt;exOMt;$gvzWc&{n4u&~^cL=gkcZK0g=*){#i3r^v_4`kF9nE>C_7;{4BC)N zvU+$jTceD-biYsDAMru2nw>piv1R1iQYG?xxMZ{%VFuH-#)% z9j)*S(Ay~-J^NE#s4r5;;{t|79jB%m1lw|;B5+YuAq5Z2_;`D3fQus~FVm_`AEH#v zL!%eXBkQ0$n}PfxFuHtMiA>EvDVbH`Cyk7ByZS#7QT`>&Qd z;>!>hg|?R{Jg`0*n8Qo&m)aIVFnQct0ECAv91BKF5ReioOEt!$KETN$nSy;{PY(*s zZ-V%G*U>u|gX3s7)Y{T%rz)+*_dZ(^hU37&8id*ILEqblsfU7KIqHq&0b`#&9h;k- zQJHW{84SW#b~E|{9qmG35ehdAD7g4|@n^es>SmvQQ=z)M1&oi$GQ>6mHSOK{xd4w+ z3r?VJE;qiPHd1uo2S!Uah2Qp_aAo@lw4hvJ5GosyzLtk4lo5E^zM(B3H`hlB96NT` z`C$Ln$ui(5UjHoUkMFK5R%XHUWJO?*#9Gqd7(pN|$ixNrhOCk!#^k2k${C-XZtAxR2n;# zjq?U#0O&7S8AmydmlaiPMXD|=EaVb3QTEAS|D<3LRr)S((+MkuEG60(0K%R{0Utb2 z)RWLhq%6;DiS?GjyRM#xCm!{$%L4ZRBiCP%o6vSxh4PrA!eJmnC7NUrX!pse0lOPB z&84Oqvo1DSiiLEwqr7TIo*7O(ZstZdLuK>z0u5!%P~?=G;Bxa&vt z)zDJq8{OOaXY>17wq|x5Ln@!EbM4H_0)>ksho>;+Hxa)f5!{GsT@lKC#-6;neR4)= z6;L1iW5!5MksYl(z}z(t=L*QI4^*Wl5=Y^e_dl|vv0n<2-p*d<*Thz#VgM3%m`8WA zL6JW8Gu8EsDKOtW{(8vaKI`RE(1Afb5s@#Tm3aIw)}E8!Qopzwej+4yAy#JvpaVvX zWbYX#`GGZt88=a2+BrIKXpbS{0!WxDhse7Z!gQXZUyy)~hCr?`C5Csk=Q7R*xK-mg zuvR6hRq&vY_6X~bCbScN1q9Wa@qs2{OIcH7Q(fo&b$C1P+M`F0!r@WH=XY_m`>h$k zYVpYUzJ)#5zf5*s>A+_@sybsnZt!-);gXm*4Gj_3JaJ_XcxdVrdh~#KB5;0q_KbBzI`PbD2?bhKoh9OkBI|Y zz_=!rBy+w|jA&$>OHF7@ot>TcdLD9%ns~*_p%^4TC`LrzX5?TanFE2b_qhXNOIpO4 zw^cT1yyU?~0o63hZ3p|%!o`adt=pfCjEqzrSRosYEI?`Kjm@X`04`?BU}q!S{wl@J z*U+spj9#!)qXnpPQRWFG2?BI1XUhfg3-lQDhnXNC7}{~?<=D}8z5U&-1$nFyPg{1pDl(+L7$M2G9I?Ii2|CYKYzZF|J$6 z1BDNuCa~__{k#?1S2^-#%Km%(L)YMyT;Vwhiz|?>I?M+kq(@Q01plzPfBvxLUxh|q zFbX53)vQXJ1jr5X&jKmZ^f>v#dJ~kJzzGD`51r@ZIl$7J%8x-2p9zo%$a0ajZ@|V%Ojl>P&ikfl1hvhxU6E^W)b}+-CWyO zd@Ds!S3tfKQMq~RmZYq~?Px4tu#dauwXFVYoQWEVA9uNvfF?sDRUQXRoHw&oHguo; zGBw539x-kgTlB)0#2*+`pLQSHEFz)?5m3y7MF13ekA?h|0uAKZXnra#l2V?kRP$tO^} z6QDLYfo~Ya8H~peNT>jRqDGb^$oQwXH*#sBav%MVK_Ol?YtJb#>1Wd$XfL5skK1=` z5p%MKmo6_*0pn&r>3-3&WT07sC&u9i8Q@~p+<$xsZ~kdqk-B_a>D}P%od$BqxA?v3kF`pw>3k?)$F?6<}x_*A8FE()(iXnVvN zFx(aWwi-IkRS~4bv6N7u2uvs$#NW%n$L4iHvfwt@?dX&REz)Bqk|tb}7zj(4n24%wxHSHsZOF%vi?8#U$ zcSI6#I1{#6*6jv-SEDcXq3&$&UX!xLG~VJ>84kA@7~-=_um0}kCAM&7%kPL02-Bp) zQS%kaO=*V3p|h=L0Z^kL5Rm0xut-ZQ!Yh5Y=Rkw!8cH1~4sE7%Om>0@_y#g3|2HM4zH_J^(5Yvn`O(Sk zW^W(d0P&vyb>*9V1P(#?6th*WjiG}$0codaSm2*BVUeO|RnyKsXR17# zl7Kq>eucZgk~sh>5fRZy`WMK^cQOT~0m05{NO43HVF_3ib6|>VemtE7)~585=AFR6 zIKaXL=ZQfw%%H-G0mPS{32TF713IlkjI9#PIAAKwnODejrB~$EV!LxkjO@{`P^r#O z&6qW2#3Of#o*-r+(-jdRXS2;ueqSAAQs8d+1VVM5LoXRP;ig@XL-ymZ)mOaTE5Z! zOZ#G3Tx`Ye{e`*$Y-ggQ`ImEw{M9*Lm#kyDln>g!^=^HL%aTc=yLZ|6M3wt$P|8bXDC@lp#MER5ECM~WQi--tv4<@`L}-_Vf;f0{9`S&qx3 z4fD!r)cbEgyt=Dbo;A1b!7%&f=1~NJw$WVVFkl*loM3|g@E>{hC1C$Hiw%Np1*;Q} zQ{sfB@gBn31%$Xw$jywZpKo|{5UXS}IR%?1P`~{beR(L#M0FXNW6w_xrI z7>A*rOm;vznpvIo-(jqn(!OxOL{w>2K+}4Q^ivQl(Hr}!3Q9*p3`&Ybcr}CqWYC($ zYTUj?$^WXPsEu6;AiE-9&FqdFx8drlV^kW$(Q^V+?emfn=>?I{x}RoCWUV-n-UY}6 zUDjA&*?NRQ@SN(u-}PJbK@9M6dvY7Jp#ltPRqZl%SvQKsB)D#$9@1YV4E#!8uq70MJe@0mn+!m=`|}f<0s6_GpEOwj zXtA1%^GyM|QO}E6PUu8 z;R?A`t1n;VEXvya+4N0VRq<_B(F&WJo}Qj$=^+&vnbvm|H+LesUD`%uo6GvY>9uakD2#q`rK2FF&m z>?R!MSU~t*R8e(cnlI@GmiEuz64r9K$i#WxTX)Po{m;xf>&0A> zmZ2~{MWc5y0lFMAmHOjad89waSMZub0cG{wQy{TyPErdIbYFkJ+TD{+uFO}>3Tf!e zR)Y)ct_8V`&zsiEIQSw~YhWGarLFAv&YPY5a;0>EYN8@u*0; zF~{6aI|5goc&u2Tf_6BuCO+?mc1Y`7q%?&JP2h~noPQTKTpuBIv7~2e{OO%>k-`;t z88%Bq_Q1zbx@Z+%6xBn;?o;la_q2F^I1wi9RrZGR$TFDnVhux3HJmti?i{m;Q_3bf zUie5!hka^Ty!JoKx&pHYAII9W`YqLWjCc}n#{RRPG9XMZS8<(zuY}jDv zRC0fj)|hD~(R|Ie<)&YGnm!K?zr}mV1PJk7FWTBwoY311LZ#nkXft4ci>gz@lO z#&F-o?ge`2QZqnK5mgC+6X)vHs~^zofHpcJsFrhyRl>MRMfBwKG)!4D}w_4yIYFOGX51Q5Uz7mo#k*#>lhs12)o!8pBXoJ3CJh8!t@%S9h1#?R^ zM?+&9(q=4c1>|!GUoEz2hrwhX*x>Q}%v^+l=U?P}wgsCyqn z{*~c*EQ)Wm9x7G5FJ}$Nx`%wR0g+dOIMJCv(-<{2&;E^Q!Lf2$#6pDxrdrYWc$BN@0t1)fgOA-K zEbrmmqP67X$`R8FbQX{)8b`~kkMyi>!dYi4q{2-cA}vp^EmkS2L-WR=Ek8qK5WTu7 zy%+rgcllRuJ@!PbrD5#gA?R3Nzjzj2oS1YZuTzUwjqoE*Ixw@ zz5$2;%JaTY#ye|0o>T)nNQ<$;R1td%Yz-GTr@hrj?hrfk*AlX112Wd4h`PwXKrHXy zeLTu!|3Y<(LgZrcWhRmk`6nqQp%p9={&Mz`>BL;- zcRrKtW!`^{DR78^;2l^ZW7~ZnES@<|7Im8O`X5zMslg^?Qs{x)A}XHmU&UpXGXif> zPzez7K$1_*uGX5PZ{U3Aa_?vlh6% zi6qQKKWRok>Xgk=i}peU?IX|!(u6cTr1oAk8CRne9iw0mC07H75+>ZfN5}0{U)4Wx zgDk*g3x!yD!_zYnZ4Ij20wg3}Rn%L!cC7ntG2bU4#9q=+RY-Ivh9rmyF(Lshtv0T# z#(uU%Cn)*vQR7vjlKHSQ<9%0GEI{(^&@EX%M`xXTyHVZfH+&^XLOoESMRpLxfE5@1 zkAnx}0j`-&BUO$c`|}Pc{&@4zil6UP$u&`7$Y{j&wVV{STEA;!%t9aIHLWD}feTb3 zx?cstT{IM$A+b;`#pyk85WI-w`vg5#xua+KRanZ5g5}`NS3ltIMQ0fz00U|_vj;+n zsJD!H{|D%8&~M0y3Kf^#`x%`zIR^)mKJKRxqJfn`!? z{?38<@2ThJs^PqM_LFF5QtrSRN#rMSGycyu;po9u z2uCAoUR@yrG%>)7sSVR=I*>YA7N>9WUH~6jWoU|Azu*G?k3e10;VjCedsoqw4_m z_9T!AIY{x*kzm(lmU|P8Ryy^!>gBnMfi;5}gcLhc513bi#vvBmESw&C~BCQm@ zva)fjcL-6+bzQBkt>{CuU6S^OBVQY4OQUQQiL_063XdrMcq9y;4?>)D&;jQi%{ut! zJf*$sGysPPVv!yUdJrg$AFvZOOey9U(otUXfOxi{ z`y`I+#wFRVac4kr@fJ$z!G{GXd@p*gZ|{BN7P8l{I-!jvpwJNH=l@Hr;Plh0C9dzt z2_-Nfr6i5-i6qjtD-?4HsCSixfMgS$eg%QX9BYa%?=_r9UQ1{t9XSVZx=x_BBPT2D zE_cvwTwGuccda;Lp)0PxVt-MKzzu=94qh>3-Wzc4P-;bFB)|i%2G6+-8^a976J}Z^ zf#$mTMD;VKgY=>rD7^Bd$W*nZgE{Xd75>uWDyVm!Y^g*a!HO#+lMILRAJmhAyEMwh zW^g4#HLRFc{dxRGx{5NM;8}Ver3cyqng4n7raI0%%XIOn8i4mG9~^`pY+C!A{-W8j zogJJPPwh+|-V9=pcSOuk_)U-Kc8FUx-pN7HzKQU{Czh$ESSw_OH{bA-Z;7{K&~DzW;uk z^N=i`UHwUjEpL1 zx6@Hgn{&lHjmq=x1-2M#eXZ1yq#C$K-LkZ{G>&R1=1ZL1`H zTD!JurS*r`@a6l#?&bOU1KKDWq$OY-T+%@vc4T-WC;?PZ5>6SYR2O+=lMB=XH-f#DV~hVz!^ff4OR&zo>bMTGp$#2P{P zl&RQ+xkFNXFXdXOb`%#-^C^N|5*;AoWi>|ZtxOWebPnpE(%09gUT@Zr2R;mKvL@(Q zCt>_dwOM)tsrg7}W$g}p9j5K*#=STNhlx~~oJ9$~vN2mn*~>57&0sFJCu_k-7#nGW z9b5^uQrANryS5m{$6Kh>GR$fO6&Qv$9zJ}CMp@V_Z?ncIM0bqAvK&XO0CqviLzp1Q z#p%^n6gmQ2A`^y|rC))i6&O#w$r-A=c_0X$m26mk}Mx6vWy)qV(f`YFN@xoce z?t8M$+G1%bzM~f=65~^2`;Pvj=&7{plxJABXd^i~p%0Dsst>>d2|`qx!+Bf_XyQ`5 zVFczdbI>hJ2I_SukfIuBx&am64blXu=}xhgJ{)384s9l;KapD z7lUOSQdo}EtQVbO9!^#~>bitVt&xA}0@+=HEbGiNr3>6>Jdc;*^u%Xa$t!UykNA(L@4fN^6{IMFQ^<2n{(o5bt-j=O{QUC+JRo`S1+_EckRCTfzd|{02IMB~ zG#ZD(=EQ^F{zVTHv}ijfiJSr{SqIvbPhcSE8H#3xt`0g7j|0C}U!FIgh$B+@(J}iL z0DCpK1!Qn@9DtJXMeIE1mWIFaq2WJ(7}T+eh6~H=uaGdsQN59ZDrG+?0hc1$VpI=k zQCFk->Er^WXRc!W+KM}_HYVt)SCv^sk%^c&?1i@KhcCi51B?=54z!+tsxDyPV@c9M zh-)MnSGi)=#l@u>&2~}hQK5bqqJ+kKPalga8ft?%oamG10+eH2eg!gE@Yi3zMc!lg zFGFkl2?|=T{M_U%xK;dHyZ8nty?A z(89u2^dsBg5lnYsmaB>OrS058Arz-s3%C*1xdc2_?)0&2@0qrjVRre3ZS#y&<+BRH znhL`PYd^n`>mRXq_&tR6UsISal+HQ+ozq4{{%h`!9)`;3Q@A}~5iibbyEnOFvgTnzSu>(bi^w5g)>?FK`>{2|y6LTuZzSGFryADKt#J=_(C4 z3YMB&X~H|e{+33vgH^17JMr{&pR>!_aM25?hh|g#vP7na_R%J1s87Nk4Km%`5Al>W5GI8i!X)fDZuhK+XF{3cYOph>*^jC zU3APemhW5%+b`I-@E^bP@aFyuZG!ACh5^2|@1D%VfJW5nZHA8c2~Zi*)fg3V1g8M1 zCJJ+&<$21AiZ)KJT43osCnBj&7G;RsRQD#MMj-D|EQ~oCKA6UEId_UJ7$8+4+d7h^ zO}&Mv8}pLJFfig6UL&#Q_!hfoT*DECacMOb_S7%w1Q@PoIRrez0x^&QYq5Iw;at3! z1Vn7&Cdj}>0pyAVwOB~cz+%g87(5Ol5&OGiR|`zbknS{SIF2`WSDH644e78{3J`z? zWQg@nocGT1c-%2QvV9PJgg`ik+rE0h!IX~be>ij{q;7NPMd2Z!bKAg=g3-dFJ$#-$ zi?u})y0BzHg@u9TL0C|D zrPHjR15((|_^NjedjIeu?BURttsUvUMABfRjRF^K#hgt9E}F~$hOc=xlwGP1Rg&1? zFUSZCr$Ya?KoT`M7ftW&(9y(1nacSq_G}Osu;PVB)FyWj^}#C4d>~+nwoswsshEs+ zWgrSrZn#x}>0h;S<$Zz?%j5l`QSVfZ_BY*ww3+~BjO4;_*YQ&djIe=a(EhN7cDbrx zwZL!WZeM7N)?h7kdN^E~rCUnD0hC5wOGZ}DX|Bz_&@qbNkQ*8S85HCWIl`rWUS~Ep zeNWg=6q+hn?>elFM!ec)6!2x8RzrA*nnKrUWdrK}fEB(&W#LBp4JZ=TFp~%~B$CK) zMtq3-VoQ?xv%;^(ZCL~gEI(cS;q*fE%!ruw}ud1qO|`p+!^ z)>>qZ!Z)I>o3?8A2N=0Vr6}U`qg8J2hlZ@U)}s?r->GLaI3K9g1ES}%5CKarIUP9W zEXmqE41!9$=HEe9XimEm&tEY4F@#*t@eeOt`C;KG&Q~|gED}4MA@141JOUtP5H3@} zPy95VO~aq7lMu~%mrqxsLnq%zqW`_gP>%o(V!9Zr_JCAskf31;3uJN|$sVuL#3gMT z_tV*64b*Qm>h3^gjNh7G z=BP)60epM-p}#PlPb7l;Vktiig>pv z{$7IOJaxMye+y>8Grm$QJ7f&v#M3N*E<+`JmM>YdOYbP$Jn~Xb767ClZF5~KuNQdj z`?Y6Z(753n!l~cn)fv3>f8hlVXDCy*Rw1w zE_c^w_f1fw#;6@~FiJr^u-^a(dE|F%WVQlSa+KN)otA z=h<%o)&(zj`;DcoWj^a<%@7d9@&i`ELx*&D%E|3sIkpVKVj)VNcaL*h><0BQQ!rZs zzKP!RD~8-KpMVw^l3fqt8-Mr6x{Gk}pu@kI!=ZLxKmo##8eBqPt!SQ;S5F&yADMhX zzN^-*jg@S3Ve#!UP|P!(2DT(bgEvEzE;Y{}EyTWiciMBhZB*N|BK*9U4qgDdU#4(W z>@X6ErsskY-8HxzvxU5UexNm`eJgZLC?>->7~iQEsQQ&{5tRm%8ySZWgW0J01|gNK z$0WV*%R;US!J7{MVItxeO%xGfZrQS>4^&S@V~ETA@ri0QiHH>#z|y(wHW}i zy?*FQ-Ro4{YNY5kjD9ViZDln$UdrpocY`~Hy0$XmiT~D5u1c=eSQNOe{@O0CuEe|} zf{>FwSlGye=J657_;pdi-0bue9wHGP1iCVsY&Hfk`|sbsU%76jqzyI1!H=SW2&BS0 z)aij?eXqm@`?WvVr@5~SZkl$9eK>UaZ9`jW%W8W^q!^w#`6b}GglE=AhS+6=%lFi= zl@F?8iyw6e2-uA6iQ>b?f%7t^DHn}-e~*mKoBu=C|8EyNyi})R0HxPal+xO08o~e~ zN;Co+HtYpvgNs#c7NU(H{wFFP>c=IU0j=zz`gh(mSxl+a6(^fn@&p&B|4w?OcFCz@ zZ@jNxzs@mtYEQw_C}eD05g)-=X-IY?c(63$jy*Nh$FLY`KrzY#zvzpff!WbS+Y&F3 zYq#}j|M}Nxup8tW1_*3v2cfQ_wYXAr47!FnNE|rLM45HyzatVJnT_UK)6^T^Xlm`i zmQ}}x!VEbiw(30%rUU~yM)`j0)|ZBbATV>T#%oi0n(nJB=zJ{cNhu{ z29Pi5M*iDRy|3K*8MiM62uTW&i zLWWKNTxOrv1E;jdF`QaGQCU-;1`Tsm)`%YLmVB+ zAFbJA*T>>jF|bbworTq&$B?J1hKMm=@$uYgoZF+W2=iaFef0bF+Y;XcM$Sg_k5n1#@ zjOY2UNZTc1i8W@rQH@PV2m+@Yf^gEL7aYt3?2iI*W`^bn8aBf+{{vuN5ECEpX7rd% zR0Pmz$4x)yF;We=jJ@=)@3k<^Qx`XD1zcg|famR|Nf~hT>FVn&TD*9hh=@pO-BEBZ zFdDJ&4r)+Sdrm?ZLy4~r$QkocjF9rXwDcJjUiRVWA&MBTjAn{ zBF(_m4a%Q1HjAElU<1EoL)(^CpGprXWk(eIPV>dimmd_vIE8BRID_FQOoI(7%G*X# z9^w~$Gt`GLG)hq0a%qK?RV-#~9mSA_Wnds0TJmn?g_fC!w(KO^HLG*rntlI}Lh-X_ zp+a+{dy#BX($UfJZHUN>If5QG@_Ug562ii7mt2B$l7!-gUjYFsAYT1C7B(I*lKK$& z$>c`tH|f}9SAo(^;FxT+G|wBEqa+t4jkq-D7a$NXRVu^u>=ie}xUQQX>oX13glm<0 zCoVDJR92t_$utgaDS`x~K?51drkVkrpT05i#OQ5xE&A}=@4jaX4UWn`rdD^X-uspB zFy7S#gAbmF4d5xXusOUhptb^fqEtzZ>KK?|zBK<0M-?Y}`OsNJR#4lDtqL%5IUy_Y zN=qrOUA@}4`A2dya1_?KJ~G9naoE^AQXfxU5ape0c_ZBb`(`8tMD)TNQG**yqsWki z<5-7rH7dM@#1N(I^vfBADCd{Pr)JL3-Ate zZzM?XZb%Omwt2`LjfJjxxD0(UcD9l)rX#tM*$K;2Vngkj_|syx-N*1BOILja2l0Ty z6K)-yqp~cJD*_u(6N*yB@I(~ccD?8pC|$YVncCH%n$b+3fYRv2tNjJk4@J0+2Z0Zu zp+IC3r&%ipF5O8y|7CJAXW4jZ{mIV+=96WxqeuiKK&Y}l9Z-hsBUou^7{|z028aBqX_nGVdN=zRNoh}h1vCe;V6`Vm@tv7j{e(fmV$TjyphmjImR<+i`QDFL zu4bDk^tw_GE<`GK#*2>yuID&->-3C4e4-hb0k8s49nCy1>6E5%8z=%*mP=BZbNTXR zWo{45=vPzn-!6RP3ZXF3XOYpNR)O7uE~1#~f@u5}e1LLM2n|2n(UlaA6c-Qc%%R;k z!2glE28~rL8zR%mLPb{SF}^A&KE^5%X-g4&p-iS62fuBX&QP6;2V?@ ziTyz4UJP$afc8={3cbH0tEH6&%K%s=Vm0uegHaADf2OG($odQrB;UM?Z5_4{IgcZ0 z(=H;l z$68hP=C{NTexHeaRp^RLHN>Ad?0>GvQ#}(o4LL<+Dp+nw579+;+5|D`P>FNyng*~L0EIFgn*kJM|E8QMBvg`m$f zmaf|l=7>K7h>KA)OV8$E`Nyd}Ws3vOZvz%nFP#HH2D3`VY z0aD|WZuv2jl@$hNAA$IYV&3ez@BK0%QC%fKo1!z$FTKCzj5axp%ZC}%tFi&j32aG`Y`}Tb@`@Wb)sSn>JwE*I;cb_JH+OWm;|9hk2?Q2LYDY%>qS9e@) zI*UP#q=3-0{s9`IoJZgWr z(ewdJ0Tasi<{5Jg;lXf=^9sIjzfmIqm15OjMBsQ(bV%@;=MLT*EY$0_b>A?DVPZ}1 z%F54Z2tcRjp2OqB@l{`vl+j)I;$!CL?Hy9c>UTP=uVBb#(`OJgTukR}(-e5442R2J z;{FHpaJnjqQ@vuif>+|`1fB$?QpZmTW?_RIx^PKBk*LBVzDmZG!?@qrbeURa|q>&r3owScz zb;7(u+6ajifrteU-7pfr?_&>&n3r3`I%5Dv?*s7?fgi(>huK;WV(>Dt0237YyPee| z8Z;+JQ9F>JZaY&}Ib6ZoI(o}xPPWBw{8{C4SwA&$r$nzo7&{0OeP5~|E$`jJqdsvi zz3uy8LPKaf1X=#gl=s5V!&XnBI*oK1whs(zp1qTQp%u!vcV%X?UL#exT{4+PLZ(y_(&YD)3)JhxYxk3k>q1)=OVaj$;9%Qd z1(y&P5}pIu*)t)))Vr_m0K$CZOb85EZ3f4R$58s%!!Q>8dgL3*6nEXZYf)ZmM$ZS= zO}%lf9mP=Ri+lS>zjmdxXgb-HvUsE57Ty4edqi<7apZz^e3yENd6D?L{h=;4X zxUZ}1_4Zt+^_azq!0zVu;_2=`$H_aG+5R=o(CxwYIiHZf8coXAI1J{v6qK@4+%Ds4 zpF}ey=?WwOj(}6}oG-R~nQ$PDETkn1#gCJcDpnBSNyjf|JAdIq2Fieu&KRi|mv{X{ z@8<1I$1nrQZFFKykDf}?$dByH31>D0@8;Zigls@;!P~kIqMWq$0-d}eVG#yB(oc-6 z_?ozmNh2c65PBD-G0J}urB1?ggm4FLhQu{f%=)5s5c)qHDI=cMGm!sDK?b6RjL9(s zw@UgFc;N{|6Fid7W6VHY%O-5K5kaiR^v32Y;N-9>!3zoC7n`9wE+b|04@50I9Np zY){`%fgKrxu!7m5h*x=HE*HZ#bzXynC8`T}xh_u5*hlcq5HBBD#3VdL*vrt+!T}hP zJc7Nn>rurfEv`TD8MO$RzDHV9a8<7RHx6&#<$*zC?awmmZ17Da#1OX*Jcnpf=AQh` zuEuRV`mQ6bS2}LfA7Aiz-HO`5E22fz=F+F&_aBn4e#K}x2VNOS=BRC~FWn2)!`p1V z{u$2L~M@P$DvCDZadyM(WP@x zP0bh7`Z_|L>W!wA!N+s3`3vkWmwNbI^D$GANIRd~x^51S7JSzfCN4fC{x8I+M0DS* zX~XP)ggo2?{kK0)I(zd&@@gKw zogEKHo_mq1jtoUi2eC#r@U=Ip9MjozN_hqzfc~~Ah|LyQj&U&x{3LNGqqvql`V9?1 z>ZG?_Twmli;NLgQ6lui+i~Kr8jLfiw0Mba*UJ1{8Ph6;-9)oM(j*xg`gTL+(sI6ucq|Jt~OX6xf z4X$iCxqNJ?t$6Y&_xmNE?yNecmCudnZ?i>dN8i6Hs`Y`xCT{p^r2e4L713G)>la2J zMAj&2tP{QWW$flw+9-icisJ%MVpr^ePI4t@gJj=`&Y{&?F`|jN2=Y0(c@)4Vp z-M=K~`!=A#&m4nyhl^P$V(8qLGJ{h4E;cI4k5y2`i8?YYER0P+;0pLj=XZ|w_F<57 z^NKR5Fp5|_GczOOW&G6FHyavY7qmB5p=2H{U#Cz@HOA)8^xGlaSz` zu(4TBeg!Ar)29&lHz9M=E?-V9rm`Mw*A25HUp{|c?&|8wuk6kS3-{+^hd0T|RRXy* z12BZv+JDR!m)yN;mnFK;Ru0m@If(TUO^P?6dOBf>2a3A5H={%tE?s;0Y{DLc0|zVy zkn45!u3i>%{``4MRBLaiO0=&gB)m+AilfUM98-YcQvUWW7ljR^)0wK38gc zJjj7}A}5o8pYP$wwv;)Z7Bj!}0VmSlbP29USyU4Y8?DUEeevrwOikDD^70M@nfi=Y zXKCsdpOY!dc^^09eeh!upwx=PhY!P#!U<7a)50RPdTU($eX}wZ-(Ht)QPnc-$+c@# zL9Lw_0445p*Y4D*s!yNz@J8I{XEuQ(6^T<>wQ5!6>(|kfv&G6sjvOIR8U-l5pPJD4 z4vlt9NI3(vucf8cjmm#GF+xqq_uf6*z(vQE8uY>eY`_0!VsmP??!W)|`~Mk8-~>T1 z-~`1{gX(~tgX1EU?{37Xx~8VxKx@sQ8?bah0MOlEO(@bW&PGJ#`YOtXkO^HjL32h5 z{^p6!WnI@C2NXC2+b1RlbUj96?S)k$pNh~Z;dguTopp7s=o@bnvN(G569b(=tPL0; z;}Z8x8M=&KViAFX%g&xX>xA)=fQgubt3Kp;;>3yT${un|Oib3iUkH@ho1}y#7QDvj zY1J}MRTC=5$s=P1L)By6K198D-!Nkp-bxS@m&#YK_VrHQE-h6-V!whEpuoJ833nC{IB%_*5uLGS;BcBH9P@`%*9YF#WONCKkLH8w~Yz( zVsLV@;Hi7l_mWwYn+97kSttO#3#lloq(ZU)^(IEv&r-E!nF#`2)*>iVC;(T+1rBTA zRG`B9Ox*5vC2%%WS63I^(9~PDZcV$X8gc1TH3D%CWG*oY2@NsMUcf-}H#^m%@U+{? zd{ljVC&!T4sxiv786$t7POJrf?VWrUAJ2myp=fbcv?(WU*Zgv7SPjZiJ{>r8C;)l} zVsoI&6`<@i7<`u@EH6))@%e#DzzGf{^msE>cLN4=+aZ{8HR788@2naPAN!U7sDwD;XVc#j2LWwSf|D{#Iy zw0s^I2n0`k<|AA@<^IBu7_tEzfi`l0~d849Y z{0<+pzGp=+lyy(e)-^T;g@x6xW)6kO<6BT5-}?3~j(}t6w-YwdJ1uO+n zlmj=A7ob;mgNHlWQM8qbnHjkx0(I2kZ&RKtdV4|tx=6RGd*HDkAm!}$K-lI$QIB5dlMDdQ4L2bmCbxB$zSL|9|Vlc z587)RU%sq@UynqAOP83SqH>|!*^N1Vl4su(G&JnBwy{Y?Sw$Yet5-2!-Pln;Q~_2t z!$<7w_~=G*asQRVJN96{;FU|k*mj_olixe zo*3Enn4O+>csGdRuVz1Ts(to0^^LDvo_BR=|IOO?8S}S-qQeHtv@@0!tr;a}SCb-p}6RtrT93yhC@K|GxMBKQ+Ym{m12Qs@GiO+p@ zLKHMQh0|y1fT7{Tv2N^MS-yNZJp+S=kTGFVjadAxs;cO|s0OFPP^$e(F zs`19gMmn~`JVuMqDdRTR(9mFxjEXb9cI{dk6d60A3Rr)XLD^n|NMnBLR5%{b7Ke83 zyLa#E8yPLv=kG_8`ei(1EAUWUohlnQZUp?j_!bnw84#&|K*q8EV&j3}_I!EKBP8S4 z*w{aA=!;oj)c!s^EW}1{?0f3K0rul1?tUP+`{4DUU$LSma~iD*g6KJjAXort7kBKw zQx^t_9QuB-V})?Q{0#C;0QeIs3Qi-rCh@A_@Ox2!Nd^LT+1lDxy?-C~mYIp^OYyJ$ zIP5GaC=#2Fie9XzW9D5q>JTkhjTyR5Kmx1u^3eXeN=;mE-#%twi(yZ|YC0r-#h#tL zXeQ|y9c4Xz`gCZY8`{2xb1wS(N5;l3Pf^gyD-l`%2%)CT9bdsoHx4G+nOZDulf3+H z+)UqIHWNIH^vS}aB1>=rlrS0=x9=a9Q9>djY7sKeps&60asWVkOODlUCf+Ud@O;~R zTKmxvaX9GKEfJ{pK@gfX5$MnzfuW#0sO>(KpjdRD{LIX#V{qt@7G6$idEVkuOo@2l z?c29oUB)o=iSD7%n&XBn<*iPzk!`uh61y1H_(jBQ&O8N(l_@(k!7IG~O{3pgPN2f@1~ zB?E`Y64X(qgM-5r=)Nh4>zK301>$dG&ffFql~9>Q0Au8CGjTYfPCx6d>^|lj1CxbG zs?AQn=OUt2(vYXJQJNrax_m^^i9nRNjWcKSXbBV;7EP==Ugmun8gMEmLP_11mzR%w zWovC+-I%ZkR+x+U{k{0Q;T?tGxiC*vY%5z1*f@Mpi_s-}dwbcef z?g7Bg?A-k9m-3FClo>gTI5X#t*CBLTWj;%#?M+%{zh=@ewYMJwqU=K#N#Ya_@1 zN>5)71COx}{FKd*qPcl^a+bC#3$S9k0m8uY_Kpsd>vL$wv|&)kutcRlPM;Q~_DXAc z^CqKnL)gE8DpHU>(DXkL6bW9D$wWMOuyr^$JDUxVEtdD(*q4^IM(^IfjhViC)gXkm z2t`}0o7;n(FS1`EW->Gj1w@YSg$iZE>vY%zWnUHG%Y)Ni@DOW3lF2B!8STe{7dJqA z*>j)h+^v+9a-NeWQ9tQvsp)C`_x#j6dX(?u$Bzd0AjeXsrlziMJIxClHm!_ImNT6h z11NI;W`$Tv@kMGJb61|~aK=}Ed2^3-L$}VV-X5D?3w^vM1$1#V^r$00SW{h! z>9`A-PA7mZ{lNq0AvhFv;s=OcNyQ^Ddb8Q!2{25KY^#ipry#YOxw#OASeTgZ+_`LM zXowUCC~e02UvPvD>Iku!&(DmT*_5$ql(LK`P{qzK8Q%mFIWyEN&x7?(wZL0<7RxZR zffGrpX2q>laW`+WVDU<*koMNCTlu%2W5V2!p3L_R4VkSf$DcY;;S5_^xQW6Z#{txL z?W7~BDtL{_gs`*U-d%R_b^~uB_bei2{gy49aPG>u+yv4TS+_30rSq{;$%Sbaz^tcO z<=FZaaOx>IrM*2bdwagTF5Mj7>Gq)Sqq=KWF>VNK%xdoL3M7@ntW917>|$hqnacHVOMSonJ6<*#?*V>eDlaJW}uGI=Q%1 z5yda*W^8N_>~aLSt+{|QF(@H{&$oAp2PGaoBcqji+^z37c3jwoH5dM9far$6gr9OT zHg?s~CM0XcWViZBs0IeA8AD&cRx~s)VbU35+)8(40o;M3r<*wbE}L-aRe2eT*ZoyR z{RS}P--(V>Z9}H7g)RO71dKfo(F(wXBTA#s&y0``z%3c7ngCA59>rlg?H>&1GuphB4-K^A^CEYc4;i< zZz5^{r0TBWl+f+IoRqYN)HQY-^t5eHPV+$$_lNeW>@h)$>ELSMqVUZPzp!z5JHy=i z05$^~uqT0!AG@Y$X5j9Vyid$fi76*GZ_bmeG7jFGV-bSlvEnTi68!mZ#W z1Pv`4xB^)O<~XN!m>IO0oMJGDfpKxGZEv7T04*XqUS#*_$wtOE`*n2G?d>H3G=vGA zf|9TY<^&BKJm22teNh-;l-DhV!Y*FrJ%$5TB9@?tG4F{HK~?aSXi=KOiMOG&?UZ1F}DYaCv)s;<9h<7cSfe z-F#i%@pycfJRAst=u`0g^+QJ_B}W8p)*+}osl-?gtz5Qi&t{B-J2x2sg}Cy=2Ojvh zDf;~OMd*w`7cvcsMlshlh-g^BC{EHhcue2gzKV|@c_HAdgLhY)#B~$FNdsA~y>H)6 zkSvIsDYtG>8yXruefl(8O-@s@%KpRMXt09IumU0=D2Q5&Wsk({j<@iO-yLo4x;o*o zJPNlbKR@5#UIx-AHXLxVsEP)7eyY|Ks>w==yLbP72*9h95HLTPGc@A>P2M&W&v zv)S3%ksqap%+8(LhM;TR>4@3yar8y`PoC_;AH@!^g;7=SywN0V&s+=`9dP+FGnR1f zN!qY!6FazL(!bS$@<#6c^myNHN={IsJM9hhA+`LDxjip zBWn=DV27@ls)1*-&9-}f-t$QVep3tTtMn#YyedpVkATy|rSkjEYMPqOn>WAgF#GV~ z!^$O}Lx&D^kB-WmdFKBw{9rTK_Wy}HX^8MSx%y(&n93mQ++W9#wE?v?jwmW7D96T6 zorJ**zNGenEv0Q9Gt?tV^tqpr;WD&AFR>iuZGXo@32~mV?r-115m|KCSZfhJ;@}IX zml1-h-s65rxl_Bh=uclDmktvQGxwy5iJ*4(3kuqmMt6vhm$w&nh5}k?wS@HS(2yR1 zn%oNhLQ=K5mZCtqg(r0K&D~dVAENq;^I#YJ0o~tHcAId(Aw#=V@mwUfN|ZFR=$ah< zRKSYzrxs^$;F*b57`#ms@)=7{wxxxD=|QH1(P~-Zjti?W@UbVLh~2=Hq-_k%UA5Af z7kE6qN0y=wl86u@jg5_@^ao%$0B6@0$Ig;giYMj%(E@37LyEb6#7{FeQRcM8cxQ5Q zk?@SWZ%IK*_Jx2|_2Nb7&n$wVr?L=R2%(bs@Zp)csnZy(5xUG5hTSp+w-Nq-%fgkY z6<5mhuEZuzUqmHTBOjC?D7-+kmN^6`WJM9!bp63V5c0--dSs1&s`n+#N1}tvkU*SC zI$GKk)Vwk0JArg4VCoSzWBG1n+_^&wjEjXgJgC!sA3TtNEL86w0Wn|Jyquz-q@)2{ z2B5a{_;CSJRDgrUMJMU`p(6-RL?Qu#(-D-;I4Fp|MjtK{M44O95al>5*9lxRi8&qp zyrm^7&@>FQewo1C9)8$k*~P%c#RXEI4p6-st}epvVy@y^aq;wq0ZbvpanrsYAMe*R z`0~PLdSNsDoenEKmKMGA)E-4eu}hkv-_bth76_xedP!l3rn%ERt5>ha(W@#ir(k?L zLhT^!D?5ApTlkx2V69_Yy*e1gvWSq7@8C&aP3+^6BhIRE=R~qS&Y;)oSNi~^`edFDIeT2ZH)bxCy7KT71VWSY;6WH(vIV{mlQp#r3|5lPfSH+@YlV6(ia&4y_w40h zmEdFz%VZgNIEROVMbc_uZvY1`Bedq&VLL!I&iWqHX?XW8BEH-L+`(lY&P$%z_WAQ? zYO%%3+Aq2dQf5Auq@{`CC$9gwN#er%p?4UEz{bP#VhB7gk-$k!NRTmz|qiiPRW|;?4W8yzbhrpdAHX zY*)|Gom1M@d^&yO=*YkE$8#&^IUR(CfBy}ehW8#(x*!3_y9O3D;;JKyPQ@$U;8hhWiP{wHoMX zupiBUYUGF%6&1Zz8H=Ci$KT1$#T5u~ig3JuQBiJjb^9~%W>5sjxJRtL13Zq23fJ7^ zwRs5hqs=-!V*nnX3c(mh+iSdb&%S*%AQ77JMzfxH_16jud^SKy5Xwhy7&PPdD0=Wa zs5WyOh+sb@#Kp-%F|H$bQ8};7Vn&gm#~mXDmQAte5YUWK3 zaxvV)-o6*JvBoARt3dd-;7hLGxS?TUQmEmz*eQk@3C77=J2q^fLrSf}PZ$R|1zH6} z%_=f;H?VRPR9OMa@jB1Vw@{!s4FMY343GhND<=sp-qEoJFKBFhyaIigALjbdv*`p` z0c(kQp;Z6DbL!M7Kny4U)l%JPZ1%_Ese!B$EI(SKZ z$mm_U)5TwkX()IiFG_q+k2R)8tl3T$C=nufe0I_nrG6qu_F+@`)`q60TKFrCn?;kG zg25W%)FvRa#c_miL#`rY=Xgqzu3n{~7<6r~p;?(RS9D?LyQJ9I=PIi3)auZhvVaBJ zwQJYRkFOk%`aKYpD3qpLKD z0C@0fZ@+cYS~eNc~g%n4X>9>WzKbM(#dY;O@``Lzwnc7Z8VVV=lpDWR9|w zTh?6dr?O6eu>7}5gX)q?b7ju_4*ScbMy1*RE;a)G&s8)W%Sh*4^!l+pDxz!A?AgPT zVkB{`UFc+_Np7;Gu|@J98~(64|MbzEr2*u$z$;f+_`Zlc!?%Cz*?BIF)W|o-TYg^6 z+70#)%_`+M?Z!u)oMd5OmmGa$_yRJ3F~*0uF;>-@9|7f z-P(HHTyj44#5W{uFQ^wjA8?2yGRC8?-@aW)Epz+&BN7Yf#7ZGU}jBz z!wU8tPTiCZUsh5Ei*oCbpcQ)(L@@$afO2Q~oiMs}@1V#Zd7}BwRo=ceBnIPRQt%uy zGo^qHoIg*FjrkT9DrS88_>r0dX@zag8fHpJXs9|^?G3|nva*pb^C#`>P)IVPSgr!I z`63ovl@^&)|Lbsk-@RO%ABC6)f5-PNa>zz~g^tqRoCcJ$+UtwXP z)Vev)a4Y4xNJDUX@-qP0W<(|zK z{pfV&j2}KG3JZm-WXvXRYim=3CY83*quNZ93yX^Mdw1^C73<;$hXf$Qx(vn4 z9y3b169`eYRJFG!F2Iu+p#?-K*anb`2>25ttAyodAt(}ADKsT6`2fy<gJ0 zNzuZROO!!MpFtQ`)b-e}6;_==4CmWw}Aj#H#_mQ9KPsCox-o+*~1LX5mr) zzAHa|{#2}JM3(QN)s(@o-rgJ}mT)){bn-W>U*C(`f#M6#dd@Tlh(#!MhP}@)p(uHo zXBf78xu#pV_hK+WQ+;nmV6WF|dRizG7G(9Ijm_4rkDaSZK`@1b43GV^+62ONb}k4U zD8UMEAh-p9!qAaIr%YgXcenF<8HQ(4ui{Elee+$z>GpO4pc19NKK`C{bDA~+7{;cS z+nwqxQQUCNV16zSvMZYOLSa^@YHYkRS<+eRb<{pt(q;aT(XL&~B~RJe5l+NlkbK9M zEnDiJiTmd-Z1iRsMv%`O9vG=c}vxE(zzJw0~a8L0-WFn<}lYT&9sR< z(2-hAPwz_gS*#KQRSN<{$MVLLC))@=z{V!TuENK`L4&MnY;4?(dWp;3=s%#;tcN}c ztN$L}Lo_t5Hr)3mUl#M>-%8Xyf4lrz!okBWy&i=O1wI2KvQF%GLI}{-KXix|3BmP%_VtnwR{v$|=*kJ9ExN<& z7boOn+lvd>gyK&iK$n&pm+%1MI#7Boev^fxqs-_?U6T9#eW3IQI+E;d{`iB*Ppy3D zRyK=9Jyx7D8a*1}qY9zpHJX~ApT3V+3x$3MiYk(Mz(w`oc`A7Q+5#FW91aULli5$j zQ|8m*y0%t&Li@w81d`+Xpu^t*pMp|mUv27H5 z7z+@nnU5Ymyo{PC=NHl)MLTFN=2#QT6jDs4e~OEYB-#I3anm6~Lz7t)go$=gAQdi5 zNl^$zvT}hYjAlcYX>kyE58Z&{ygWQTke041x!M;lM8>!`KgGGT{fKf#7Usdm!Odqt zAW3e`G~lF&e_wC(+uf0wnQ8xNmC3sfjMz28a4gz#W@TDm8bBWrH+?yLp{zs zjXozb2f0;kze;ih=*#TEY=q0iu^PajJ>Pw7Kvq03E#4-H=`DR_ON$w=7g71fUYGVZ z9YCO>gqdXlCTA|-(O?@1;B zCKJr8g3qEd`3cYwB$x#-BW@choe!4Xs6x2T~$w58+e@Ce~GBnaZ=Fa3@5_6f<=6S*(VrU zXpSKpnUB)amSM6Iq=*;Ar&QDMH&?xTw~CNXZih?x08ngf#>aw_W8B+pF7tMcG<}C9 z-wD%vfW_mpHPi8&~fw+dk_>7=(g7We<|MABkZD&!{A1iT>n)I*+ z+$2cDB{|f`M}lW14;NQW{Vl#7D*`q2w9TROx8Ps`LGY(9C9! zRcaBKZ3YPT0%=1C4cvCwKiXs)_a(!+&wv`RacbXnS@oeob>!VAUSQ4=?Rtg7UsuXz z+$wPm?=sir905se)~Jl5{2YF~o{fR3+c0lRoiwlp2}tpUoU5aJ1*8ggqE*}R zVY-rJaA2U$cb}aR$q&SMgiy+cA~)@OCG*($A8ma`k zpt}(wGcPv{TA_R*d`)do2tE?LArj+K|AKS2iN0Uwe7wjB{jY0qIOWcr0Q@c&@3!My zK$hf2Bqk-n3`L=Ucy2=wa@%hH)Aly#R%9fNg9i_Gnx-2;c>2urfc&h|t?+C2v@R?x z47^*7=gG~-mvLS0T0-} zcA{9T2Z?gc&8=S=RH52W+AvQY@ciYVFWC6`F9E^6Ea6XXswF7%G9JU3?z&X{5X6n&QUK;b zR2Ci(Om5l@-BhtB64F^ zh4xdx3H7(awik-9>yuQzzVH58*cFMFk%3~726^K7hpBY!Ir~vQ|g+8?B4?YS? z`P?p+5DosF{A<>t<1H<`@t<-Kd9o=8H|A%2vxmAHG$K? z3F8j**fO?YQp*XO1Mc?~#v?sj7FVgtaI9JLYDI&WA< zNd*z`i{@}Ey?Z_723Dqc9L~z+*gsZ&7vuMwVi6WAv5hh%0~cj!F=6vVm<6zuxE#cq zEOyhr8$l&T2L6XN@&3o*S7rIXy!D+Muw$^o(Xb08B6hJcxVcR`dOInTZwo024c4VD zN0Z6GN5S!m`@Wu4^2jNk#i%H=K_45LAqYd3lN@zm=L|M1^PDy4><~K)Y>f>gQPI&N z1#ke2`Nd>>3zF^He1I)kEV1vXp}u~3J<1lYBTvB2WIuUw;wLH3HFndpzsH6fqbs4I zQS`DB5ioBtl8sLl;7P{7NN{p;cB5285_LqJyEc6{It!}8 z_Xy|BP)yqkgex*F9hv60341U@VhE%H1?6&ggQ(~k>Qx@f0wpCSkh^Ivac}b9?1<_Y z7)Eh&5w5I6KCw_bZp`Imz@fNCzg0>H1rRXAi5?F;D;$OsQE2|x5>Cj9}P7tyH`pP-)%_nL0owryX~iiSaE zD~=vL3TN(WVk1h?fh29j!pyuA)u^p09+a(3OJQL2U>&Be9Z6M@6cfuXH&lVAcUuh+ zL)Wig|3|`|Bvu1M!+}=l=ZwgiKSnzE(EgI?1yU_>R_l{ECj(hr3CQN93&1DAht&nzfip9c+H(7 zMrd5ciOv#x;t6Z)X86s`7!+aVkA|69^4q+;yr_f($$~}TeW#%brWBC?wu$dWsM+AK zqT&obz4yizola8`ON@J8H!6Kia`R>mxJtP(Ir%WNBK!2>!UZFv0|%m}rK7gD6gE2U z7?x92O^(^A?$0I6z{IYIW#c9Cp`N!%Nwp$TiOjT)mGM}6A1(k3IIFV3z5%pTary|Z zQ%y!7cNyZ7cc=!TrjUA|H+2vGywWSUV13|KP%>OqGDS=WXe2%%e04DI)_TEPMkE4JUuRh#G3g`npNjxzKu$yopz&))7xk`i} z+a0~{HzJVN)q|GMfG1t|`zI)YZQjwVN{vB>NPxg3@BwKk<`<77m=E|H33qP}7=qxR zZHMANJ3rt0C(xJ9@P?LAtNh20sW|E4!UYe4v40okJm?uC#mSAuTeCc-Hy zMPDcbu|*B2?><;zHQgc0^YCUE3$54ZKQ1nCi;+T9faC+Zo!ppJC;<2z1bqdbgJ7tk zme8?g5JW@;BK`w-80<~eo?m71KSw*D?&O!|RHp0NJmX~)1w};*Y+jiddA&ai+Ek_|&I14f9enJ>4+qW~&_^F*WeH^u4mirq4JBQL zhJ61|pKrLf#1u{`Z)rIoLOX8z2I>KQpvIS>p<){x|(VWVmmiN!`+c#lS7_8mbl$y{YPV{LH z$Mv)IkeOvYCi>-QE&yjK_8cNN0@NC25536$gXlz<=s>Aa4;vX7 z`QyfyIUZmXm@V!|W1$YQTXqBR;kRo+#A?An9Y}}i4Yl~h(!_7EB1cM0Obww4m@EQq zN7}T2QL}`{{!)A zm6RxB2?{X-&?BBgiAg9ZYCH&Qod9??(tJY5t@u(pQh0!V-vy&e3TmkMRovVmaAgD7 z?HA#fMY({-Qwu~+6h7E~AJ;Er)fHhKpg(Gqde31<6_)6qri5MOKps+jgGbK-JbM4Z zgV^Fn`0zxi#OTE+URjftzI#g3N0H*dEjghxMQi6yog`39%=q~CP4d=Lgy2wtnZXa( z5Us0H>=N;Ka7Y+>*S(G$xlvunf?OtLl1mFJTNC{U#9>QtJmzDt;>5D?@xA6?&o%Y=72m3}D^gkoOAl+kaD|dXbe7J9 zJ-@%8^cLtlxK;SwM%w*A`<4`6MCD5CX()Zb?EC(%T>JSB>g!W{e0+>nFWbg3tFv$4 zKr4(r$A(IXg&)tjzr$t~Hx*|x>($+DHb{$-Z(i9_QuXO6e+p*?5B&Y9OWeo(-@Jpi zXE4~6Ys2_FLQtl~;nb;@mD0#P?EL&p$crA10<+8GuU}V#XAl)Tw6;ghxzHV;8C^tE z0b#6NZ(_7k9Wm;G$?wIradLC3g|k8QF31vGxBB7)WUhl`qRr!g@1Z`URAFUq*QPr( ziBhopO6HiA4&!zN$Mu8H>2nFM4ghL2;t75AWc$-ict!dcwgc-Y8}`PDZB<794Ljk1 zA3og3_~T_`<8uUIo1w!vW0EEN?;J2Nc;NKK8pHTn&{&!N2FS$1!QuGOwoe~E80wZu z=8d3UID9gxx9jm8uc??-M@)FFtoo1{4+C#BE^LDsey(FJDR%{S;8#-%D7*XO?GTGK z&Q5QM_fgn^P!&IJ_!&lLO6?mK#sIIzMfd=6BPVBO+#6pxz5JGuSla``JD1*ZO*_^DM%EQr3gFbI-^>lTs zkaAm6HR%ffxM1S0d_3^83?kNn11+mph=yLiTtgTJs0`~iY;e)oYJI@iSnRPWS}M>7 zU{&!9xB-gs9W&Ik_) zD;*u()CUF%z-kKGWJaY4OF4kC24bN8Bqrxk;*+izjMgi!9DMm7n$JXMyI?2E3gg*R ziDsE!#wSIIg@WhA5kqwNkcu1Go?Spd`i&gqBa|+_PtN^ZPW_iQ^sqIzcQ7#=nkOvq ztq+gJspPGUZ~NM2zCTkYJe_*%@gj{@C@cY<3+17nLH6zq~> zFh;+RkJ&Z+sed&z)fY?lZ-}~iv#}nD=s~LzfV$IY=MK^qf~ye{9!>x~T#EJt%=l zT|w|%30v$Y-U_^j7=ivAiZ9+Khb|-&17|()thsdJ*!Cfo z5X}L}4#p8y3VuA4_JF&0Pdxtd74VCDtNo$C*iEvsY9N|YA3Z_^zzh*8Mkxz&2V*f4 zD{C$EWO&E>?7Is3=U_rJPEXN21G^w5G*qBfzzObmG-)q`b~DkrWr7*e?Kx;#V#$K6 zV1WW0zw&oLkCb3zjabdPd3_Di??ohXE8r41bHz|{nPH4|Tt~IKt}Y|GQv>|`sL*KE z42zbB+w8sZA72j|EgJMhsNP0YCUOjjs2Y?$P7uGQcHF4#Q%7ir1QM8ITN0Iy zo1313taTA{t4fN0RGpvbUvcmF+X``Fb1|-k$+h&?IV=?u1`z!9>(@!h7mzl>kvqvL z0-#bS=^a@v?I(Jjx?o3$xTTHECdTGP7PP0UW`wSEwZ{aG+-VEYWgsc(hkN?(gQInX5iWC=_3c zj+Xi%KqkHBJsyBNq;JXk#0lcaB>Htx(fPo4!esw^o--xqeyb)6$e`+a zJRNEIVL3qaKRvkT6|L(c?O9{lmc69Bu9BXdoP^6T6bVcSz1KrZ#4Iy9+T^eN65BFQ zk&Y#t;9A5!(qRSqm%L_Dkt@=^{ih`>;MT3-Dq3h%DR4$=?83B%P3MN&>^rgoLNKFv zImjvV6DN!|etU_|N&Ky_sr5gzyAy_rEdo?ueKB9x9hhw4oxVa{04E05_-O9LuSb(R zh^2?0pWhhnEDHIZZUx@gGI2GnxfyPjJ-!I#tNl zZx8&pngpK?H%3lndcrjGS4Yak$UD}rNb@UC1DM1J99x_jw+ej%K|<#BG%^M{jd);T zs6?lL@t1y(yFFjN)O`lnTuGHkxJpE_!U`^8EWy|q;>aU5G0X?nfP@?cvJ~Z<)~;Pf zx)fdTN7y{Y%gBwEzo}} zzk~gFjv^@vK2uU}@v(_X%gC@|Y>*l_bBaSYfTS?kzULYOAXYHyhzZVDCIRK^93v*N zm6aN#Z5XP>04?2v^mLSIR0Oo>vn6jq1N=qU)GDj0s3`x{3-UoR31rDFqOEiQ!E^>+5)3Fb_*D8b^ytD#xP(^__d2Yk zwn?yWQYesS+|QrC^;a)W#Zx>N6zqTZ;>4mC=h4}=E(|UyosW+Z8JDzd6mJh;6GwgfVe2$b zCsM^Nn8tBhSTg8VbULrYHE-g;0hECxK7%2@;fL6SEa&D|g^YJt} z|7efVMDaio5)w*j^+*e3B+gIBa){IF=`XQ_mmRdc)aFDXbQ=-p?8Wug9YI9iX=;jK z_Ke#}dUL#d{`oMfc{nM~xPfLe^24t&NXWifG@`Z&42HS2bpU*3x6mStKw4PsyM&sr zyrRPItt9WcLd=qdnJuqKjQa~L8l*5qnRrLq*2?N4DA<_dMI1DgyE@Na{3Vq}AnR4a z>jtS5YMd&p8`yS60nvpFm(zR?rIm=RY|v6F-@!N~Web8$E%-ePd=MlQLnY(v zJD>v4n!AVjz&y;6PB>#|tgpgb5ebk*%!gUr+!Y=rFi6*a8KeH3fzx0NhLb9|$>7{2 zF2)3D0hNP@$OHs}2|V&7?+z|i4&@~-DDGMuZ)Z$W5;F!X8p-hdFf^^ro>9~p?`}eE z7|VM${oXw~6cMC6N6pYZ?tT)QY00#c*7Gb9L^L>?v zmWTLC`Il=28hLnm!$?K|$TB=GBWzZWN0@{8Lq>-W9@Ip|-ruoNqcjAMJ9`32b`0;{ z-QB&e_^5NtwQJQX2SH3Tpqk*kwzE$D0U`kDwT0ao2PGVV`2`Fa6bdNG9yrZYW}Y*E zZ>Q7utUA!*M#hKMk?Q5|e2d7anexA#rt`8$E{Z>KNeMK&cJrpRE5+4t`7?Oh%x*o* zt%$do1x|j+uO=OiNVJE!7@QL^dXnJ+@$XzPQ(6i`c|O-}#7Rah%*Gyb>`;tV5{#(5 zh?|Ye6Nn_W79}Cnc~CBzk4N|a{ek*zQPdMM9e!QtB>osa+D+RZ^8>O;=o2&v5c))J z*^d&>+3164X^1K;L*DGBU5f_TLC+uw6=q`z>vtu8#n52+EVC& zz0iFDBxSl$p1WtHhmIW$!Zjb|4VE7)m9Ja zsde>W(5`PiwJPKA;3g?=z4+R`oLsS#-HE8!&_&vUo565;8r~QSTiaU2{o-u&ve>NH zKQK^%M%87J%V}x5(a=tTbr5;4x(MC;-wvNUcP@>_7)O{U{?XPtK zw!llPtgEAkW`btDF}!jYe`x?C33gxIx`=B0B{gF6#diqR2oSn0B4jNATD1f z`aox2c^Yk+#-*Nxdn;l@9k+>`YPLlB2Pa=OgzAepcVVxPSnJH9M znCYk?QxIbbVFc01o&6JbBr;AxPIZ?F0Do0I!0U1fteK`mmko%oTkne{x-TRr-IZ|& zZ@1PKq8DB=oH|bhNjVwh_39>lgKab>|33P+=*lOK%A?1wj@} zHE}5>Hgl<+*M6i8_KW{06#P75j_3& zj0@vG2bDzD|0#q6A_cYJ-Ne}R7j{QWJryzSV-q}R_<(F6*GV;l0v}_kgoI*!Dntdqf7ZaQO z%q`j!7vispKlb+nLN4X#?>nZ8M8qlPXfI$b@fOzk=Jj6iUq-#TLjl8+V~(_ zbs8cZe_Rp~ZOQ|jV1YrjH-CrEBL81P1kjby$P{=0e#jJwPXMNR@DAZesfOMrs-gGk z@u|Z@zp6F2W$^xZi=h`r(%%$BRD)4e3yX_~PTpJ(-Oiyy39~Rb$isx34n?=jUg5#L zdm9)F-oAd_8^G@Mjf^)ybVB9E4FMA2y3Q>KA|wjE#3yJDs|R#ANGO)dS{tHk($do6 zFvVz`S`kG$Gs%>M1>7fcx(R28q$%8ju?w#X3j=1Dss1xK2OJXkZ_W-Do?;Fzu4MpB zSi8nf`ZAHxL0((|c0E&{RapoGOw3F0(ZmzPPkPxa>`<` z*0!f+cTi%01HZfX8_`+X+uIdR!&c!3vpbpPLm^};ymE}hoaG75L)@hU{ENXSlsT9M z8jeR!q!8?Se%i{W_s^a5Gyx(d<0TYM13X{l{U!Dq?g7HcllF(}PjGAy^k`sau;{sF z!=vmj>)V@9ik3w}65-!g;7#F0cVT_R4GyDo&`_{-fEwshsu~`iWq7ap1_r&EODOmf zi}b~$wi@dkIr#!3>J@s}kYT?-E6RW>p7SyJDnMUet~*=G)9CN;n^@bxmWZ}~ER+1I znr8&{HZdwx^0&7cwe@hv^#usuNJzj2U24(`@g11=!~hfx91{KxWLYu+!wBnz z*HP5e)%`XTfQ17>g8pXP#%^TSY9NRn_%-9Rte@pVa5K5zk3u{0>eZ_FQozx39Ng&A zgeaYD_tuMvrhW37D2*TlI%YH_A+Kv3J({q2BbosbG9bfBk+W<+X8mF^gd&S+gXB!& z(U5pV8oTmgmOi5)rd$8&mCXR|d-~+HDJ3y+QPIE{2(40Y={xqL0?2Ym!KtI9V2$ScUN+ zVw;sy*8OQmHa0P_fGY<+`GgYruf=Ose2?707rrfRgg)%~`1DHVu+;|+AC9wAJYXK6 zfS3z+pKieZci^Z{$o1!^rKRaGb*~mb?W6?H5nw$deDmpKT3~#+w zUEFKP$M^wxIC|A!A;!CWmST$bLL7oWH3AZrIVvu6c4M&f*cc%eb1bTsV@yrXR5aCj z1aSA6u~e;-v10A7U%g@`oxNTdO(u0mMnU2FV?m{}+9K_GoYqFKVX8n&?E02PjB*Iw zMuOurbcg-Mqv4K6UD;VxuAb|}kPAAnk%+RYf)G zDA6Tr{u2?-$fKAFoI5+5pS1e(AKA+k_o6ie?uhy!RQZ8u2ed8HcIB@UjZwB0y9G}x zx=|7I%yG{X?Vtr_=j8a|*rNBnG9I?~EBMbw?_a-`m>l1_WeahV$_=u!nnj(v;tC80 z&J@mUakry2n#E$n6SX}v}!3j1$M>YW=r#8}N3Fy9QPxPI#FGHNO~sEMuP z7u^qV8V7@oFdfUyV=c|{2b2q4xf+U9n+O)#(@4H5AeB_8F5H4H-S|~{dI@U~F z&B@L#CxF)!(+;Xb*q_495dTtKQql_B%fO`j!oK#~5kEcr;UWyw*=ot49U9NVC#UH0 zSvyl6`4IMzas)2pPr%ii0JNb@@3@t+5pW*H6zACedU`>ZFYilV=*+gbs5ORJUw5lL zfd(Rd?2)pJ9{ zkt{AM5|Nf>LF5WRk$kG)$&=3w)1p@_QWUrk-6@eRg1^K9Plwo1i2E#l6*@z~Ihf<3 z54-XS2sGaP_VsHIa?se+R1m~(TNC_=&;)uxW`V!)i1%V+UE)-&UBMZA3JtNV)W&lT z#DbH_%_YRqp!|zz(G44dUG@#2(35eC$5?oH!gGFMs^GdxKQw`W?ld0Sx(fIb4MCQm z3KQQSGE7qOx)HN_tDreOOmB1P+e^Nj$~O7_h(WaM#WHVCrA}V%G>P_m%JK z>LPYS4MSOA`x7`Q?)+rFpy94vL0U%v_RH$6!biVhG5|O>5tIt>FF~}gK~V%YPICO_ zUV8Dx&hf{`GXf5gAv&9Y86cipAV82yLpW8X<12*UI=l-^YvcG9em(BYbyq)X*MBhV zgv+359e2Mx!;D&-96Aq16_IW9Hn z5Cvq(#`19B>4QV&Uy}Xuvvnc6;y#dq=NC9)i66%d4T(;Tvee}B0LC@frDp+{qneg> z^@b=CTJcR(Q=)nS)IcH|O%xW-p$D9ddviy}?cq=R|H?oU8zH{+w$@aAp`Ya)9v+sx zF!hIC0-FE9kzvdeo!%S@NHSkX?-QDKoiL=Ef5-W29OvUfVxf42H&r`v1FlqmKy)aq z9p(TPnGO~fR2Y2%sa-7WT^SbHfF&SfG2l_DL0cQfPd)!U{_|%IgeKz!*wi2jipg`! z%E_$+k7!$=26~!wttA|vNYr;d`JW@;cmQzd%9(4RV%Q8IH1q*~to+8s5UAiiuK-U@ zt$!_?u(BACDKTAWEw%ya4V8~V(?Jk0#3gM6#zl#eO*t3^0lF7)4B1JTIH+NX!q7gw z-c1P2%~!kZHftkOri&fJto9i(jv1Bqa7q13+f02O--$s%0u%M3d*Xxr|j)9 zO(H(8Axfe!aWs_ZkCbw#UfEguBxGhzB)Lh(bqzihu{&m9@6ThU zn?@7NY(7a8HLwqb@&~X|+j;;hLls;+z5GwWH(&)+1W3Fd{7YP2;`qk7Q<#K*hhX{mouRA6}$WC&E*B(X%VTK55?-ene_Lb2%c>~wW=+l}Z#v>kvU3!HKyjuEO3 z#MEN``=}Md{TI^Uk45(k?C(T}Ake9wj&oA(%E2y|PrKpV`v)}5+H($cQ2mo{09a-_ z7wjJdm9DvFk@84t^bzb6PR`D7No|#tC4G3XqhA8H1Bmm z7OqCGE0h>QlwfATe8xyH84m)Xj!X;Pvj=1XLo-_VF$@J>Mi$(`L8jq2Lr6?8Z~1{* zCU$)SA^?HOvU4%X)l12eJm)aY@**JFwW3Bygt#3-N=m$V9-2sI=9ZS!K;276(`FoVnJ{oA)SxO4TU4zO#(v9?@axvm66WLhIB>gKWWjL_nI zb&?qROos*m;<7$dI0W$QK}f4>H!l7b25y7axCk?xg;aWguP!kuD*5$$Qj@2;vi*!q5=suS6>so z15XPJb);VY)BOMC+qbnC*SPEp_K68?-O2^B=(ki_<|FfS93HQ~Cel^coS}0K4`wx5 zJwACBniAR@H)8N46cayei^#SE-gmVA4~QzC23i)JV0!|@CT2a!nv;(=^T0jha+}!Ki9or4>GA*^bb52-rjIBN zV2$5?OD|;Zwr3aQAy2_n767It*A7$T{Vral!6XJOfnl9WM31xu@F70F`Q-6J=Rl52 zCfFc}*$ju>yFvv=VS>M|=t2fWvg0)oQq2w?`T?m8oD4mEeT2ON*vUNyDi<}r;U^c} z{tU3d2%X)>v^vGLg_qz=lA;=$8mg) z4`jwz{vLGM4>CE}IF!c~3GNL{0l-vzsv;Uwy6-!&aLc-RnDj8D^1h^GtoE)Arfj2l zmOw8=_XA^LSNCI4UU@^h^yJAsS+3jb`t|E4Jw0X08Ws;+6jK24CTu>;-rVXS=+a%x zgRJk+fB3sWJVeKSQ;ypY7qTY`^}CUk>GRvEhJ2p%A%$eHl1m^AcH|~ZCy=RbH=oo3*npNhx-vM|8LM0hvA$R}EsF+{cZpfT&KjsZpq$Behu z!l2Kg-Ub|U0J5P^`=&SuP@S=l5%?_)y*YiT(2RwIY~J5;QC5^~FPiDDdo!9fZ7TW) z3K637{Psg9FajTOk4{ja?83R=%q<`eaiSOic0B{1z@)p^xO?N~5GCZ_yv4HVtZO7? zl)tiRiqu(G5BV)a1sDy>g1Bo>{6`msoq!_d41zOscRx0EZ-;d~dNBpUN6Z&I09!(E z4N8%>_|3sT7rFH)Kb%AGi@Zqa6>ng}2{2qL%urxD0FMJVjUp`D0Ovj+zK>^!f>4!) z+#jV8vl)(m=Qm|eRk)-yY1Y#G!DHMVV*v>5^3D2UGP-^K&HNDKUPd_*4w6l13@-7YPbE7Za3p54Fl3)BY-WMcgx-U4%T+#J3}q*7i+lL+12`kl&<>aH%P4~M6#!Mx z$p}xtd^>+^&$xQ}N@&iZoKD(ww4)2w)bSb8a(dTa%6zvh^CBKt*AMTd>kNlK>7PRQDDi}>9K0~CX zTL{%Jg##YN#TJ{_nGm2K$i~LTmQjAK?_^h}hnc4rIPQqFsHs7QQNlLsl(hrhxDX-} zz9HnA(NkTC`cr}alg8~r3fxBibKrmC5Y=CPemW8pW8+O<&3K9XUl!-&G#xa^OUu8U z6OL2Igv(t=4t!+>Rw52G^@)*LWV^O)f3mU}8MohBgKqsrPUcKpAbI8{4&s@hQgxZC zgj5A*6k5+4af7c@?96}Gh_@9a0Z-O1&1xVO5>H<=JN{9${d|-!d%Za!9VN&?w@IJB z@o{<9aHG~ZJfq@!QYSCDaQQOh{Ouh3D|98Hy~61}&7LXbyfH z{D3}vMp@m1-~OC=^Jw~{uL&N($vSpF8Rz@<%Nk`a5 z=-4R7#n7liOT zEw}XKH;~=eSY|HV+*8HiI<1PB|HEi*4y8Z#_8lcc z5FosfxO;LL^@KRNy|QyCFfp#7%1ts|tf_hO%y!Btv`Ll<_2QK)HFIjC06IiD#f4mT(b*Sr@IFMu zacAf5-1o$ZV8INlJBKU&nvcE4S05I;pJT~pWjGfo+ElbAC*cO)mQPn|V6kfF77t~Y z-BQalz85#X4uy{JU(#eA<<9+MBR>N}AL|yX-;*06!ZbCTxs_G!zScrLiD?*J(M)=R ziwq76B>+obpTLX9-fv%xX0T6{S2A9JQ!`06064vVL zl{j7UJH8GUKz(&kpr2pMR;^m`DPMnvl->>mL^8O~HU(f8x!SHc z?eSM4{OJnO`8Q4R{rqwkqr_<4-b~5jxuOR8Y396n!Z`4A{kxc$3vc6H^FRXR?SA|P zovcthx8ikrTwHVLI+wP_())A_fMoGT5N>FM6cUr}|Aa6^d2`RUEfIFkw=d0qkuHB~ zpZQiI%wuK|*jQ5>vV>h*YES)qeP>c_Uot>7O)v8|;$JDh-2D8H^kB$5@@}w7$(zP6 zJz^t&^nkmeFDz84TD$JODi&DqJ6ki+r3m*Lt0;afT*nON*8Q_kXfn8X5PAp1ROqjf z2WQD;~r!RH7mT3_#u-+UcJzBT`cD`PzaM-*&=itzV$=)AyA!Fuz8vqEvfc6RIw58}!{E~5brdM|La9gPi2v?}e02KcuE=0|iDr z*;K!td%}4ChO-x=qj=>Kqcy4xcF5}>6$xF}x_ z3)}L`N&2TYP|Wi6+|+B(AS2(#%+nKF-2F#Q{&sv$4(b zYH5i9kDk{=)23%d2}*%J1c1sbQity{KaN$U|K{N=pHF#P(Xre;CZ zspY2ehd^C$V0d;mk)P77+px-rY$(0=1qFwg4xC>?MrtW*cy~!rqj2iDNYV2mIeF!j z<%-)r_hvIi6A2Ty!GFK9v<8(I5;s)Ut_tsvpD_r3tL;^f&RR;sGY4K-GO}Y2CTU!@ zyK!3U2HW({Y3ci>@D(;$&I^K%{Zrz9s?};3sq{~q^YiylmI}>|zais0<=6S>%`P|{ zB7kam$`6@@;~2SdB}!LLwMFK`*`oquiJ;Oit+ACqd_h$LTX5BUK|Pg;9IeqFe>mKoB$UDfp;C!p3VSGNCo`*^!A^lk5n>rVdtLR!MrS`9@cZ&}d!61I3CAtGrLn`U#@ z7*iIO5^a3~_fe{^;zJkWR$}^$nQlIrg;bLe$3zkV7)6?5{&2f9*NtXxeshDSF&x<~ zJQfX!p*2-gXRQ9#56%FdzXsJJgi16px!Lm%CFQ3!x_BKzOqRdaXmRlJ4{B9aRVMj` zl=Mu`J@QkDysxEC`5=e@8BBmsae^nwj*mkNbbvgo(ipR(~+fqIz!1 zq)ED~UMz+oRjA~uE@<9&!32l89f>piiJ*&%w-NVGXrqeQMWBtdqXBsYP%D-B@#9Ca zOe26c>*NZa9Urpu)Kb6%-;Pd>IOXuoO}clQbr-i257PerpGW<0(-`DO0F$zn*VYV> z*BTJl>dLmFG?lzu`ojkSu)D6-&+JVpXN6$^cS`De6`3jb<+yJxMm~;g_UA3#b0$%V zDvvEcz7yBI*yZwb)bUH_Z@QyKj=VtqNykzWcZ}*oP*Fh8c=J1UGZ)+p4Z6(I8?8m! zgf>GlxQ`c&5%H7lW~7lIQJ$iDm&~BJ&mX=RA*RqP2V%jY5_I&}+!ntlT%AQ(gr%qe zlt9VA&VjK-=6l+~(8Y!9#Sp4~y-*n#&SOc>BXnI$>cIiD@@@IKBSOsyTSBTuR+1hc zf~^gT)b}4g?4}y~NRPba+Nu`QMtGe$b4Vy2scEzZ1yZ~P)j?;Ty`=y({AL zNa<3?yO8IF_f38=aSc;(iAMt_EQjM&G&CNO^+Z{f$#9gQewoPlNTLwN$M&7~yCq!W zxUgL-;~{mveB*7H(Ep*b{aRU>`>YX!rR`NOa%SASamF3yCQ{e}V_|ZtdVfc*sMV<& zM{Q}A9)e45Hi~9rvo%|PuupO(NPT8tB+GpC=N7Q z8Df()3In}24l++Eto6}4=gE!BfV0CS@|q3KF|3K(4F;qaPCT`Lz+>N%6W0cFnTT0W zi;9a?+e-x{wEc3_;$iRjY#*voHUfMm4ETC=|@v43)UOZJVqBL=?*;Mx49`HHGYUwy;5% zK5g2vp*J*qpevH#DK5nHa4S`Kio;vChS8FLKuKn7by4$io5Zaa7NTWQD8`UQ ztgjaeI+j0y?={0?KxnYJE(efAUwu$NwvdmkRFYWIDY5~ z^$BuD&ux%{(7fj7wdr9XvbvCL)^6laMC_ordtX?1Sdh_-t#)l`q%2%0_%(D>s9m*1;`5hr&5nw2l_2zJ0MZHY=%*t{1hih++@ zr`_j!NIOJ1CgoEI4D?ML6CvBW`@vmY7Su6YMh%o3WkKn}-4mP-?7Ovd=XVpyq<*9D`bh3RKATluL$Fq_`DD0)iV2ZXiZsgCv-r}HounRqTI1A z_fxB#UuU)KRPh;v69<8B{WlV+MbpDzf-u%+l4jS0C^7wHJ+;jc7paEKp8to$#%M&_ zy9-rSKkn;yIz=qt4L;#TRG8)Aed!XE-`8{puW!TJu}URcpD;On#DyB$VKxRx+BJ$c zUu_-GF=Fh^+^iOVQtG~wAqW0L9(JUmy+cRcgRLFkShYHJB0B-mSzQ`iHy;E<+f*(n zvdg)63`Ev-YNr$Nvyb)7HCpflB$NJ6VI#XZ(r>9t?-e7&K8d%H+CsCsM4X4Z?2owc zQazYWP!QCv)oaqKYJ2mWIN-Oh5U7wuf)Su+HLaw~wR>Qyb@bYd;h1c+QyzqouxG=O zgLV!oFof(9oVojY=Rqf1VMZ@F60xX#bmbGuQtAy6j)e&U;EB$GzgRg8w} z-J(AS^80j)4olX6++cq@+KuDX+7fw<_UCDD*O5XEFaykRkj1MdFXES4ahRa z2oM&d(zFa5LPm>mHv`4Ni#&f!a{eitJ=AJd(-~s=LBH;ip07bOf#(Z%M80?xrDTwS zWz;B?02H7>=aTY=p>jj9bP)SDVKcvZ^JZZlDO8-A6$DIOn+D|PGF|pO1~3s!Uq)(b zEFmbNh$7nTo>S5?{frzTDO1Rvvr=y;?@@9jq}L|a)u6u;fFwpY5AxcyZ@-Tb%s>j6 z>l-Yw<`A=VCg+INbErp!9|AvA>>D$42k0Ad$f&OG@t`T$V~+iZU;=fyuhrp$c2)&1 zcJEhosbKI4)pJ-466seR+NfrCUpSFfmu~4S>=U-YdeOLCg7?QBpM*LYuLHcSJi^Xn zs({Co#;W?9`z{!DdR_>JcricTLIR_6dWo>+GtQi6ZEYR2JafaH&``~x&-Chcx;-Y% zzQD~)7~vsU@GQQ>a}E4DNp;rzbo`OT=19+5{~sX@T<>s?=ufsCKXlC+Ice(Dr8yIl zGIYL$%@MGpy_e4MANeL1ew3fO5n5il`$zH8%=~_{M#pNzA8@f+w*KEt5=K{S=>5+< zqlgW-G<~mE6Uop)(GTU5rs*#4H^gT&TiBnpSs~NsM=J^7%YBidqEHe9z2zC}ar-@I zZcGt&tsDf;nTsGq&&Y3Cb#tTkllz)rJ~{2r3x9s(O!CeCYO(^{8yxBVjg4sY5%+pd zF5A5!rRBbsxXFS{d)}h%x}TkWzT2)o)O)xsB-Tj8+DA_thL}*BiF#Y37D3;5d}KA2 ztyr<*W=VVsSo5&4V~?fdsV$5%z_rDAEk?%=>2ZZ7om{=3{T;D_SBwLhVmo^VghA|^ zCCSkq=;v%98=r*dAUDr1Sxl|xuS_d1P-b8gr`9uC9BGw?9#N=3kOK>=2>H}wj*j7h7z1<-Av2bhh(1t3zIHW30d6C2*+juvaP*{ST4w>EG!ufvC`2b(ZlT zy`GKJ$Bl#E-Ngu4Al9&}c?PJDZ>Cj4=!q@M;7?zYJQZU7TC{4l^?@=$y)C$4K=Jqq z6UI#Xu`u)A6@$oIY1V)5-nHw~^H_;+QvIb4Ubzv0F37ly*}}}ponXn(Wf@zQ&A87h zG)WAJ^tpZ>LKv;h%US33yp0)>hbXg!TBFE?!;{%KmlUqiN@GOYla^&Bb+PQ}$pZ~nA6 zY^{3Yr2E{o#BCg}**T5sjNOU(uz>nD9XqnQ^@2WfqQm{_fiG0nuUHE_V4~#PW#`7D z?=&@~(4Zi4>pf*t|H$(m7;=Q?>1U7kzIbsrs3IoT+_;C29_8GA=Mc-KWcacp{UK2N zi5RJ>#c9E$8j6 z{^4l2kDqLNRu^1Nn~WONdi$}HUV5ClB|F51adu|QPoLF*k(qosBIDC8{^}kFSiz%b zgkdf9#tJhtM^qk{;FtNN=7HU^!6>PZ*~sf+!P$l2E9V`gp^FDue6b2~_v5A-?GOQo{@ydrsdi0)b)*IAm3)8iSV}w7T z@FUE*D)kv^!ovqsS>OwmON!QviAFAhAmg+678uA-Nw>yg3 zM*3cUms$4)mX({K#uvIux?Z-XQdCz@hvgAA*?}XZq(Z-uBWSNiU3&)muq6D3#}xaG zFdg>+GhR<}XI2dF&6fDbQT?AjvlAlV^HL#M5pSPNDi()WBV$CWS-fc~F`< z&dU9-pg*8=&sjc=1-_mu0(jRkIGJtTswhEh+meR21B}Sawe0qxCQiJ^e&AJ=&^$v> zLvz-aOFMNtzZTe4G(4hyxD-aCx`=%ytS3Ij;DIWTDoQ)Y1s zbAvt-n*{C}S1#Mo8_LjgMT!%P~m@lel!z&7lk~|21o2%cD42a-POHlEc|82(P-`H+4kAG0V?rKT6-RYXY9?TfuJZ*G-L*;(iO_P~*Ws?$ejxn}-c613@`XtU4G>VFg7 z3lL!_R3di~i|gp1kl}sYHw6OtdXv@?HYB&@f+r3hf%zOx7p}E;D0#shJS%k%k_uMB zjSEl44M*`dTE5&ftr|nPviLze#Y7A`JvJphK!2>ztduopNAc=dP5eODBKD!Zy1{7@ zuIySFV>`DNOXVk;z3MCxYl4_s`T$vF7R61=lPPEIV9@gL>uDv+h;-Z`eS2KaWj-V3 z={^}Jo%L_L=!hr}mxnhwlI6yRmr@Z)2r!xF8FnoHKcIs#`!V3A+xCO9v= z0A9UMPjMY{0>uu!oA$=qf4MUHOau^!hn-x~IT;uL26yQ1Z~(lv5A|Z2$2ynxlbauj zb&5-xeYBNP!fB{hVo%a@)p!k%OniXOhPbKCt0+46%HVpxNp zXV(+nh-5Jjm&AaMC5ISIKO8!c_L~u)o)N7im?bkza4+p1d;dZ$p2S2!I_l+>S9_ZB zI@V;gLwU;z#=W2e7I}DHx1+dyX41W+6c5fqXpX!d^}fqeqAKG$y1 zqe2{<4O>X7l{jmj>i2ny1zPMq1*Z4(C^}r)Pf1A$Ab&RG0<(#)BCN(R!_Ii;fE#A7 z1&yvnyePhM{e#LMdHUk`n>RpR%!5w{>xZ=z?nwMqWxTxpiaRumj!8nm7Xd&nQhaL7 zRtHwOMsqFJSaQaAsGj-N1O-p~9>`%SBLz_-T)oHv@)>eSGdeLG4*eoy@XZj!L&hghR<86+GqWp;lqTrKK@z_u9WB z4=xp8!OR9CU$U!wDT7)CSe+gZ;IM+P;`k`4L(6{1wxQqGqSxn&#eXRFVa{&+{LTjwX8T!xVYk!$3na!hd z7xs4eQmk3CM&Nv6t`p_hO)bDD^9yKCfdxT@f0b z@rZ{GSIsI5{m6k?U#coZTt~Y7SbqN7aj^zk4DeE>e-2xtg3S5{%n-p~bm?=qBJePw z)s2phPJ8EzdSErO2j7<6e`|@>@47khUt*$RlkOT0j60F;G^E_6^1n%QewsH$Jmc0e z96zSgx;I8W{|)Pn zMB-#NLrJp3+WJ0bGDUZD_IBEx)5M9+WDo89vz3Q17Q(40274Y9`{m=VtBYTOyZdy! z+wC^nW>dEeZZCmfGqpD@lfH3CR(+N>E$cp-*I zYCoU9un#VXMAQqgPN0{K{ z&7yV#`4Q>s>`RfZgz9`5aZ7kP^_Mr7&`_VADj#~c0k_}2T=CPrKmTj@+c#fQhUYmC z#+EUj!syO2PxDzgHUY?oCpUabYOf%62gJi}l=a2JBA)#gj|1i0Ta!0vofoyt=m5Ej+rU zq$IGv+IP^`fD-(M#mWm*F=9R_e4rfV<~D=%pl_eonvz=YSmd@v2DcxW4S=f|vip#~ z=jU^5S29Dh%Qo!px8U1{zn9ZXN(Uch<=1r!#E+1Uv9mhRd?iCIwk>g^U`bF6(Hyyt zs)adsg%A2*E6lxLNhxpE{oSt3IO_=8fQ`OcFM@YAHZTGx(X6Wx8YmR<^6|d%7(1|$ zi-q9Jat=7~A+4U0+3d*X;QZ3kPN0o-SbcbIxW!2M%H2NXiO)8lh%_r0hk~vB9s#4H z6)pG_+_{1S&FM^fFlL3(zE- zS`(WU;7!7EN!N@SU)iXeE8jVii=suF{sp>TQ`69Q`qAhXM0g7 zEQsd&1%vU#N!qvNc0K2ikdU+&UqLt@;P@pJoQisshx*|+s@m)GY32|qW!^d?ltWu2 zHod|tD6CBHq5FEZHHrdUE>0JY_laV24Dk#-kyxVvu$s_+@+c<^hRWVtO6lFFkM6oG z+8uF{0P0(iaeD4^#|2Wjs#MjIC|9C@NWZsv^n$vuQsBbC8^Of=59mH{5y-a-?qa`| zZelk}0ps#ZnEF}p)_lqG88alDq-$i+!!vg38xyJ0{xn^?sF$4Y<~Eq}yJ9m%%n0cIj^OS2XmLaBn=_nX|F!%?5}#>LI86-A!V=%=!r zQ|O>klMTYA)K|8VPW{<=rG8wzpyw#_=yJqCM;kxdN&#R@Oe69_(pZLqZ?o4qIeW3Wn0B z>ESzzkL9|vLuT}7BO~`~f}x$VGoQx<&kNHCHNw2HTT|ik8O_3mZt}ufM@*nd%prNi z3?|wdA;^{ppE%!52jQn86Ng5$IZH9izQ?Z_MmwL}*a&d-HxD|nV);3|hbZTpNx(Ee z@>G#2GvSFqmK=C(R{#JI;!qvG&g1#nL9CS68hbOkp{5-ire`&Z2C-zAsBnF-`Y8lZ z+-B8PS#;ZZ&5sfnT)G91K1x=yxHSQDw714QW8quW({JP#qyesdmn_rIiFcid7tk)O zO2m?KCEM@FUWSX+cxb8~G#v@ibdAHN71FEe!^Xv;{9;-QqRZv=I4V#+=L6l`^N@sY zvfPdrB8_qrPzmFf?lwDim^ZyhsrR1Y_6P2NGhKdI3{}ez36jF|MvRTouG{&7G%YBX z$s3_e#c;(h{s1vzZzKEkMr}9bZBys&+xq-nLc)<27nuehB!-=sd=%iI{!1iD zw6^IlOX=xjA3oe)o+)z-7ee~R7JyTfI-=|oI|sqyJ^aDdK8I=9p%7o`#(P~;x`?9n zqZ)y|@l^*|WJbbqQY&NF#+@#4zeG&c!s_Q~o9nVi;mj%dM|63=tP6B49qvO;PJ(AC zpXft0s7nm$%EiVPk(wuu#WGoFbcvzK)G3Ye*-`OT%NrX9MQ_ggp%=UFRF~niyj|C} zZl@}z;C#YUxg(=dHS(2P7FaBG?w-lV@(_%h5mJLxc}`wVXQxTQ(Ws{ zz1eJXcE{Vrx!=N1FeJaW(&5d#Z9BzkNWaka%lOeRY)hj|YO2QFvZyIl z`|?Sjt#1BQW9L5D@R}B%9NirYCXW*^fRWrbN|$f0M}&yHCHJhFL{VSEM#} zSVZGDi8`joNX;3XvBa;c24i$CUm(F;=JDFpmBs7cF<TZ z^th>8a`w^Mm{l3aO|P#w&AE5|FqOHN7u<=P%o_Rn`CUPzu%0w{{CvJ+e`UK@tvAyf z1Z26%pelR0Bgu->6zkQ`x8Kf%M4Ty+Cm(ymX?-QT* znDIFV0xiv%H!qML^$NY`78#|Wm(QQ8lXVg+25zbfTN%L@Slih2t$cgcq>bd~Kb`#E zR<|Ag>%Z|xt=;G0;nA|wK(m_tbC!{v)7TuvM4L-tU);~`WINPu6wfKbrt*r|JZV60 z=A?6BMiZBji)QsantMKbF(pZ5wryzuq5R0vqo>o?>zCRaT~hyLTTnXdFddcw}VplM!BNV?yp)ys@pUs#?5c z$r&sUFQAg0??jd4N9kS6i_%=R>e7*3<78RKq*V2GD-Y)7#!@SK%ghL?Km?&%H+B*u z8#Kimm~CFevh-cN6@zP4l{jemzh%5S^pl>y{oMVeL7nA%6uoJG8M6%rq53q+iV)&as?Ejh>U+_k@tZu=V;pNi;WsZ zJixm*n1H^F2)vkvvPZvu8=BYXFbNtz({0LAB-*GSy!ji~F;nMfe^vA1o(6XM1ybVq zjq@0@=+Zvmq8sSkz>8k(U&bp+Cr=_l)}+iSxfk=kjDJ)3eFn$2LTxS zK}U+sBy(Sw(9d4KVZwmWQG*x09Nv3Ba*i(V5aGN@aT%>+&F8SB?gIw~Gh?6|VrW2X zv28vw`xQe2@vOrvT{7 zR_d&&fCDw}_jV!;j)`0i8Kw;s4XUBbEeOAJXC&soWZBSmA)IvImhF45XnW%{gwF`( z1?EDhE)89=d;Iy=I|r3yM@5dBQIdDzDK)-_^3IfZ(S@Z|Xtmf*^SJekgl7Q1R&YT* zM(Y%~mTnn*`_+NY*+JA^3sh8+H%Hg6t(hP*c<>Cd6oHH))u+*KX#EcaL2~Uk!6sdW zbaW~-*|?3S+qMN&O9|KB0x2zlXAYglRM+WDu>=)Uc-x*bJK}B6=`s$)S__Y47Dhzn z)|K9?)u7}(a`uF)t71O*h&- zDJL;kP*{(G6K&h3O)^NKRMro*YEH6R8q>)*e2F+?;9!$~Zo7`p{8R0dA}tG@Xdi9g>`HVLcp^BaesC2ciI*U@(pjzBJeR4HnPxUS zO}YA0x0{>aWxvXx##H67j9zppZfvsvGsJT~_ezL=n-zFT`rBoCFH(`LPDtdX~VbjSYnc}g)Go*mF2t1g^g zZ|P^0?Zz3WSAD%d9mDq`e(;mO;=X~K>9OxZZ8ZuBTIbVObOJ8L542UoGAZ>_@0-18uOh7CGRH?z_)ua|*QCn(Z=dDGVa_q%J zbV;T#q-PCFy-z4>!4q37-yUv}`}q-8tdpo8hu!$jr@Hix`9)aoP5a`(zDCiZ#14Ln zPGJlZf|O%_#d2TX-8#;j@3%Wa%uIjFPYNNU(da(k6ig;yOlE!C^sKwqVP(?pDaQsE zqlFkY5*rC!Fv3SBN!)>o?{7A%Iw{wFo}hkiYP!6bWb1R_Qv==;H7we$xSgnEmR(!SP|!m^-~bDUQG2kbH7|iHzU4DfLM^8^nN=vCTNhiUsrMi`1z; zVgt#g<`<4Xq!x?lfrH zwjhK*LDaVE7^n8L?s;?$&9oj5`g({U`oJjYCeMS4+JfBx6< z@%8S|OXC|pPot29m0H_t+ola<02{PH;;8kIS@P9&`m{9~P7dv?`QS*@>dE`qn%!MI()tkHP{B-1PS zS7qBSWRz@D!^OY;{9%lD&;pmy{Owk*d9;ICUUuIOd#ffoQJFxc|2&bQ;hs3w>QO{m z+|}R}>LTCVf8fAqJbG-ZbKNRaRL5maejE%6IvPvW0R|Hy8_zwP>?}&e@KD>eEQT7N zNjU1$(j_aAbP-HBsJJZOnI9%PjxF^ZkEP)jzD(hY8b(Eng+u)JdEauH*2@f|ttocT z&d#pkTb*59{b(=pe+>N6;!TDE^s3+2YtM8pYtH0KK7MI6(Dw8A$O}&^w!C=}5_DP1 z_XyY8;;DkDV96rFHF{_Bxmh~c^1q*dtBhTyzdL-4n9B=_rHBDzGShHy)UGwN<^2TQ$a3LJ*Q;vm`#f$3MKTQ-Fx?57m?(D z-X>vPX;x&$y2jdfjfsC1XgMXgj`bMR&t9fD9y~YOH4t?a}y7xzq8bGP^^xZons+i{~UI(}5pd^#4+}uNFP_JGlZI7!p zmR~2}L~JW@Z{T(Iy&tLN=5+b}k$sq$`50GLAP3v5EvWa`T#~7*pNU&@l`}|%4+5FAL?iU3;5aN_b4OlFHvqrQyYX({|YN5GQ&(xh!^R8+`koV z{2p1~ex-Y#HBF3)iaPf72_I{}+U?BDx8G|>WtonKrG3T`N5_f#4O+Hr>3;mSY3f`~ zLRa;G;ZMQwersa&?_0~AtQxmRwu!i#J1o^aCaVM3AD0!y zL`O~9@JXAqm-jsSFxmHQ3O(Z`(eDAS<1QGv@kMUbHpW@J>xr7vB0Oug7{#Rsn;yWi z(N~U#Xh9I?*ZJaSa8}zDdt?V~LyyR6pv&|U?V`#Pr`>KJp;i^LvtE@>@~Pe#e^#+g z-kfds&VKIu4?36rZsWa?lt4*m+_y9Ar-OFy?>tc#PtPD2zBkXUwr)-3eSP}y;UYr? zy+2anB)CLuoIMpDW8$i~*4i(8uafOH@UZ+MCOCO|n%zJeCg-`j-=R@itskgU!^&$v zHkdJE*Uyahu>rFB*!ND;>sGY=c6w8D88+Y=5$OfS?C|45tB+@>vPL=9P}$v zmY*Hyp8w@bFu$Lv#th#Wlt>g|!e{?(Uivqyy`eAibeOz@UAF5UX|{e8R-PczYa>>7Q+>vap= zCqCKq93HnIJpX*E_Ch6Jum$%A`W}-utW`XLnrPy=D$a^YWCJDln%+a-PyuM0Z?e(! zThq`e5jc}*XY!FP8HGfBcL$r-B_9K7BwF6(Ci^*Pc=;Y&^yx{K`^^paY=Zg)Jw6>d zz@Vcz#gZ7MIj^LD{?|YM+VP$!s?InVt_hryG5G}+{MoKnT4rWJQcqG-Q@dxobi38W z=j{FgO6$l?zTBZ=e=i3ukY`=;RBFAAt!*I1ue*}%Erbr<+%Jzki38^%UYVQ;+_BYC zp=tmcR}_tl(A{YB|LXVM$TB>_-R#KA z4yvbpkm8HlZ&Nlxr(@CH)1&Cf^?h(4EO;_Sqj= zS^Cb1M5IZ(JbrDzBc}b-UVG2$r0%6`fNcmqa3{wP?A6PkIxhZ|x8<#!u502}T`MS0 znm(mI+=c_3T5LY0o=(TF0nW$M^c>L9`1)}3a^?L85B6JW$APNhTBPG*#yegJYw+kU z33Ne3m!4;Fcd#D4^u}e=5@ZX{&rXOBb|o)lDuoo9?QL6C^2X%G)^9S?6ck3$Pa~6m zA+{u9il17>rMgUoUX<#80?z}BlAGbxCbud=AA7KDUJr0#?z{B#^i3LYJd@B1ZG3%Q z6j_s%Hj}$VWhJ}{o_kGy_eE({zn>dF2;S;Wj`>-7Fl(Zj@rDer_dYjJJ^~wt)j^Op z?nI9RlbCh(FuXm&%T8`yt!Zu0a4D%X54M;*clX7!BS5r=gy-?+2R^eYN*jf%a%S)7 ztlCUqAuPtn*GxHRke64RIa$6!B&Qa7nGVXC2NHp!-+q7E91!R9%2%5-&ifur4!YdJ zy~oWNr$%fKjE}glVAT2NA$;Ft)<0+R)2_|Zi>vkOqIb?HT$ZeR&t>`6(4oJM>aJ-- z-<3rRskM`(MNID0JurQJ^M%5LJLBb8@Sx;G8N0%nKQF_e>4xyL>GaZw9sDut0|+zj zJ7KM(z{ggOOpU#~=84nX+LgBFwjPDQ1+tom&{{+W2Jn)}Bt5!xxhM`gpTFCQ6DKJ2 zOAEBhT5sKUuQJ3_%n4FjPOPC0>T|%EjHQP2-Ca|>>s6gZ|4Fr-m0F7f_L;)cBgli# zez;4x?N{Q_tVTcx+)@=M7)LWlY!J0Qa?}NF53g4Ah19Gd3@2gR5{Ss{(O3EL+*`=ER#P0 zR`PXzgu-W0K+MP^^{(mV*KN?C%{ROpY6_P}x^6s^2FNzRzIeMX7*&6RJ+ZG281P>y z$+?Q15%O*7C)4_EyIc29(2J+?6iV`VwCnivzg1Ob0N4T?Z15oCWtl`$IWi=%^7ere zud$%430t!GHlp}c%Yu_x{i(=+RfV73++7CH@#0;sz2mpBwMU89QK#zC^JdLH0Z(jj zcN_T0ZJ;Gdzst=IPije^YQ^YOmI07npn(Daxj`{sf3{I@1k##kF1`fRrv(I9{wYk$d5n#%` z0%_uo6b6Re(}G`cIeBu$Gkx$ifU*r0i?1#f)pa5LqF?c=mhB(K#m(0`?v;y>XU^QY z?tA7To0>%J3-KHv@|_iU(^bTN`9E%s(z$#xd&kEP-h;Y!y#%5?3ZHTwP6|@ZvzM79Btji&N2e(%kE3h0sVr$7-B}1jNP5_&$BEB#v zAsVgJGaFwS*O;i0#ExB~JrZ3AcyJ1g)K!3rKx*Kk6yKwT_8<~lMJSqAdv;O5CCWE? zs7-wXbpo6U!`?GrJ0)uaIquV^Pd%}#kO8UGKU6tYyxp24N)S;(a!&ji>{2IPh{J$eX^7~j zchvt2Rr~rr`oPb!mSZaW_3Ec9RrN|1_kM-rUk7~q6P~EZZFHT5BEQdDs^8^be_u^tCMy*t`52T?TAz%Otjw$ zd6k-?z>v>->ZH!@5njzfeD>ba^#0nHy^&)kxQ!bJkE6XV@2YSkRLD+Uwq@HkQAMh) z=F$d`A$@6J))Uo4IA>d+oT3=P^m7om3M}W%^D%f@j*Y9EE4*T)S1osA8T?B}NdLRT;QT57^;cKLje?8&?G*> zO+jG#qW{>RK&IWm&78X;o1-@jvlx{WK>M|AN8iZ(_;DB%hDr)bM1VvN;v{?yQkUSu z2&C@|Mm0u8=9#77OT>(2+a9Bx3*8qgSNkMq!x8e9{fX{l1`m#TwU=LrVQQyM`{uu~ zDY=>=*EqXuMZ?;2hpy~wWay6u(Hwx*Jd^E zd0!>wb|*HSur((Sm-4E2Qw&QztV%M|TW@%Kd$U(zcxIICZ8EUGnL%w{l~}P6W@!k? zdh8YrtY`^UFyB2Um9^u99RQl2wNn}tr}qFB?4aq_UMJk%kSjZvC_VfC9>W5_J{(Uo;BH^P)3eN zT{<5E(Qnr+OC%bi#}O8v&zTIGbEJECt_{y_($&*kV_u>_l_%0g3_F>am1RJHJ8d~k zZ4+Y?x0k=0yyh$Se=z_+a;LU!Q;=y|0u3(<-okiU$?LFrG%cExN{(d}nvs!X>lHBO zaySrAs}6<}jIDJbhDP52j``2Zm&-Qyv7JWqp;TXyde+dJqC|9lQiY_{(b|5FVEBUj zOjQIAKqyN5Qx~;I;j7grl<;UBJWMjdfOIFFwIeTe?~v5l;~7l`R?eI#I6{L6%Tq{f z`G~wpE|(d9**3jhk8y^Iiw!+^#_Qm61f|zyIdSnK9Q!&dM0ds_;O~R!y7bYqo}`qb z0U#Aisa#=Ntgw0^%{wK-&$vn@az)$!IA4&4@_g7UZGqx#xWP1*>5WvQZder5^9j~` zI5=oipSiD5DhtvV=oojVlTuLM8dPto1Xynf|0`JW3DyFGiA&H19~Dk*+Gm}205+tL z6*`|6J5HGR0QMDOyf0Mcn% zahKn6sm$GhbAYTcg}^eQeb~;jhrPsjN$Q!B{51H2bnKBLB`-V>=!_aZd@fqN3=;g- zT>Yj$BWO%)?5MHzW48ZWP(^K^$>W+(*{Xf-3kYwfMxaBGyuUXA<0I_-LyjMm3Q4uGB+Pl9&*Im;r8PL|hs>jBZC!9K< z;Ab~)Vi0Lz6=jJTfZ!l3EUq(OD+C;{+iPskQOFjfCzyB)iXV;2oYK4H8V7ctg+&#o zXCUs{Z-N`2PkATIlSLCu12*z!Gc7U;22w(Ju)tL&fCvS3G+U6df(z@>!}wW;jve(t z2cWkmq4Eolh{&tCm%RYMTck&bGhy?f7dR=_np$c!X`b54A8a(Uh4xx0Sy>HozAg-` z&@8O3!+%Q{J>O_9?hVGKotP%{PKF~t1yd|H|E9eqPc#{eOc>n;`U9-jqat0!D{j+)@VqgwqxwY%S;gj+wjjyy#FY+;* zG;P|?g9YehyXT({>-S0g8Z+!?H}yCI@0VgofyaLxGDRA=9)u;>XoAw3@=g0pa~e)U zDk6|FMby?;aYrb3Aiuy#^8PsVwIS+2w&z6~I#e4+@r1T__dMGJUk3qcprQz~7eqLM z%iMwzIpGgU7XJVu}$I^c51ZYXC zIH9MZ(9J7wQbSNK3ZjP(9~zt9qxm7k`3gcL*;myG4qa6B)DPoS%awkeNh=iha8hRz zBL{i;+V1{+3cil~_2K_{d;G8B*Vd8E`QI7(Z9X2G()vDMai>glk( zH&K;8jEn326QmTfHYy_>v@HBBs$fFf#fQvIzgPQZnz-Yc7~}`(1xf|LC5f-bizm0Z zI1tsCd_-Dp=JSe;*ZW$4j9_28q`KYqynz9gJ#EP@T7@wQm@iLo~&zX--$N*I^g01*u^NZCkq z6*rY9dR#X5cv-lm$=VY45dB5%92iQr~+Y-Mn4tES?o*l zot7H7*2LQFn-)-Xk#1O+_tRf3=NVDztV*`;!uy{8 zJm~-Y*0=QY%hQmN3&@jM5Wm%c>AI-!1{nN-TbG${vHblm{^PsvM0+A+q{fkzts6aB z5-%c(pj(M-rrZ%x;qXlJ3$A~0r-{7+EHPUDx{tT&@4w9=?sf97i}0_%_tODzmybJt z{l?>a6ct6$jrAett@HXQ)qV~Wg{-$~Q;CWbV66yebeA<3J4SdS4HMKc#n$}kjddKc znX>Fp`4{Wa_UGRiqx3Mt^!HDn(ea1S4E+h5mct6Jld9%6Vm&|^{`1S<@3ue&XT>67 z{4^dd@m=83-=L>NQLBM{MJi6{OHry+P#y=8?vBg0Q&`c@;`QpRO(jJO4vqTx7woW< zpZ3qG*`YdQ;}I^HMCI?9IxnJ1jS|y_OS=7fr+2F8AGbYK{`D(4H~71qu*Gv<_yWjN z7pF(>+&Ry!rR0RO$KJ85{qy4|Dn!V9nql+8IO4xTlYlulZX|`_YM~!hTKeH9pCf+4 ze2$s7&_BL#vfjpe3b3 zH*$kMY6M|*wHc+AqV}&>zGHCszplxS-v9Q5e|^QtjY5lzc-^sqf3fEE011`*QcQ!2 zf2bGB6j(HImEtNPK7L+@z7tH@Kmo8)DL;P8WfcsvSWCF)N64A#SiP&sY0R6!8!&Ztl@ z?YD{(o9(YMN;Zi)JlXg6VR`*8X5!b|`2FvgVLy5JOh_Mr?t81+tia$S07wQs_v{1l zi-VS2r0JdSgswRVq07^U`;AJTA3+ukJiqF=?675MJ67GGUt&3CAo+9ch7NQ;$oUF= z-3%=%N)#<}a&jy=g@S%e_MPtpn&gj|ejh>)VAZ3ZvLIJhn^AvNT`w_BZ|?raM(sK3B4e+Xh4bYQZo&$ zb9*3WAC1$zQw1w{izuhISGfK@Ec$;6vwwf@l9*uyoa2d^G!s^q3Y{FmrO|vUPy$8K z8~a@oyak&+g!Ig(-+dc_baK{p0GoMj_VLf3KTqp~evCCWxe|w1bylsTmdL8<`4MwT4T=r_gr=xICXPTFZ4>u(pgpb&`xC6OX@40wipd~ zb6&9T0-OTyKP|N}D?*np==s;T(0?N*PSoKTiUI|2Tct%r60DsjW)v^5Z+f_FaPP*3+7oe*P287(k91Sg55!W*1^g?A((msigA2Dx3 z_GZ!3;LPAX9dkY|t8sOmz{QEBM^2n@lhS^bOUURB?Lv7r5=)NQ^4mZ`oOy0_4^_+i1hPcV@3odQ1#uYF-Y4sEChF1a_9-gyf&M)o9VSJCK5f z>mFIEv_M3$MXyb~qDm%gD(1%jo(w@aBLfRScqm3w<8u16_VLmXfiwQU>b?Xn=lpHA z8Dq?t!9+-#En5+lv}iFS5@kz~HcFdRT18rBBeYObX+vadL22KNHkI~8n<&~#X`@u< zx}WDMG2^`d_q^}_ocHvZsXRT)Z~1=j?|onQbzR?Q`Y3I&E65!*JtWN@Us#oV*rph5 z?9IXBCCWlZeM$q(a=A>iNV4+S-8lMY8t0k~NL22^O>-^0iWo^eKVwKb5ADYir92Gk zi5B7F{Zi0z>EOKz*w(2o@P&Eq>q|i`(B~rSsx< zKv9P(efOpEuD3aX=c5q;ZXbB`8){6aQ&~V()?(8|)LJ`mk8QJn`$WlGi4tT+1SODM zmPZc;p9z*Z)V0M{VOQerpiYeKgHCZ|L;wrJU!vBTAPcR0m4~fJAm2{ zR*r3V;8m{A4x$c21IoW*S55bX#-M_ zavDiOQB;Nvs|e=_>fDP_kDKp0($TqI6!B8Din?G>(fJwM(O%!^InDy@Q4P5?gkLHE ze4?xavB<)s#FTI=FPd#1gy(m(@^Tn@`IJLh!)=8wB(G#<4nwy^J-D zYv>`UAvgsMkp9*og5w3PSnJ9WbM>Lr=tlTJlkXsib$Pv)aY;x>1Nzq-Ax%4lgYl9C zq|uP{(3rf!sC2zb z#txGvt!PM#Rhtm1hSh0v9bikyC8PZ-KrZOO0*sKh2gCnWM@KlipaxveIl)n&$U`L+ z0z7{Ut^yIENrDB1&*NL8&`BW2=LhVt8w_{ha>A^rn?KjOrLi&8*_n;O@xUHXNXGaV!DHgQvtfYF)EUa>pfs5)h?nIVR(?aw|`QkrZlhDH+y z4tw*1(b#wC=rQrR%=wnRgo#rz|25&e^luPE9c+cMMh(Y#AAnZ*x3F|>uG77by)pGE zKx#V;+c=<=6PlJQH}Y<=h7C;TOn^~vNgTZbI8QXdE5!it6m)w>D+cZ$5QYvV&q9tQ zI+-&C%PT?ZSY8iYo(pLNM#NzZMD_jR$mnol)CHrw8%+)eY_Jq630gxrmNkNiRHk`G zV`sRKWXz7lKKO_}v}seL#tPy4Nb&oEM?YjeJB|B5lmM{!^1O!t?`vgdWtjl4%-m-d z-mxX-pbx73r`FF=dnq|oAE@#Nhh{rMKe+iaD#-ZsRwBV4#f)s%9c zf0+!Z!l4s@d$I~kx(sGTCAc(;Gxf$3Tfj}BKyj2Xa@OR398z!>RsAZ_H% zLK`8lWq-_@Cd3JYmO((gmr*_&LndZmgx661i5$?BCs9(Oe>-V5JHT(y!kz>|oh*9# z`So7_6zsmi^IOrc5*b7ogz%@_wBQHurTehoR0wis2F3zKj6p$AIMA#HJ4CxE)r!;e z9kB(NnqMYh@>}SR$|HL1#vB8vu8gRm1VBL*DCSy}dx;5qaU<4Mq2Y-;mRtTFI!Th! zxQ&pE3ghg~hF`xX1w|S5A}RTR)-F-Hsdj}ZE9zt*9BobD?QFa7D&{ zOQtU>DGd0l(D>vQ6cljAPP>w%0aL8C#43NrZJu-$nGR6svDSvtLa!Y%eS1RNpyuW3 zfD^#VGTQS?#R-#x#7|cc_}2UxLwM~`9yimF=Q^a!8xr2DZg6^824C* zh@nDsnm5SSft~7C;ZI2^p zDOEg}b^wgiYwXwTqHw|tLS8B=azu;s_m9ooa{C48Z$%`YwA^%rN*94rEA7-kA1-y{Y|Ngv#4 z4o5@V7xa>+x*JDkgv4O;C|0W6HHDT49VDIuN@Ddo1*yi$yJZ53Z@aqU&aMBEdFx*B zjbQ;3;wb&fw+lqrVrUg0Xwh{=-JQ)b$FPmq)>YuYhW3bv802x{ey+;xBiUu>2v#p@ zQY0QnC=BHaKGAwRF=a1WnCplum$O!>kVuIXaFE359YHS*Ibnct5C+}XUw;jovgr=! zp1C&au535g2N`Z;u*}}~*FmEWS#A5JfrH%j0*a)B=`(%I zZ}pgH#kQFB=8_dm3UKP{raeRuu*FhR+YQkFF5o8Wv+}nf*IUOMTl?5QQVSPrwveX8 z-p=&`0y#$2r;1q^?||r+ZplGh&T@nhB|TW$6rllaJoj5LMz}&`FzcxCMznJ+Urhu; z(x#FmU*jRi@||g=*lgl@Z2&rmkbFE^OYPLelpPGR6!5we!+jNf08Cduj>p9#S_N|N z>j;8+NvObD;SU>Hfh}!{z_+`D_=^g!s@X8~t)TPX6 zbDsNeYugMroHMpU_^U{t`5ft^+9$g=IpAW4-lydXWkCf5(2$oEM@?(C?~HHSlVoQx z4dx*v9D`&!6f*^i^eu=dw5f$?vNrH5V#J`Fq*`X{)~%lWsG+jgM&dr5fGiwK=Spha zVMW*rP724YS$cUEP;+1{IM_>u3+UzF)r_~KaDpYwO5cO}nh<9`YKA>-+rtX&60l{X z(MLe=qik`PNaC`@>EV4JHnq50?@Zg zCNN5QaCXkB!(5;>-T{$&F;wM>#b5%P@@%VHTzw!7BnF&bwwdV!zG<8;)ga)aV4z$? zkc?I{>ntPrcyB{}y;57v?Yyr=K^-~#hxWHqruhG*Yd(9;Aob;t?KT5g-%rA4aNnb0 z@)5j)mN6AHd z5j#0KnbxIf6}t_*1bf9*669XeO)gQys+Qf&w@v)HRSSyLRbcq2-rl_lVS|K9B-}?C zOd>4e2Sc1;Q!QrtyK0GCTQMeEUYx;+<)Nr{S{}|rh#|2K(oe%SZIPiLlPYn5y+sQ1 z{eH1Bm3z~5!1tdT5g+9j463`rSet6YZh84XpuV_?OA0JwI}&oMF_2~k4^D=%leKfe zN=HCfMPI`9s&mW2W1q6*&UkoKK#8RL%}O(zI@y5m--en1JWRo2rdb(GY71Bba9A%>gyfIP0g5I72@WIm4BfdCNHy@G z{h&u0huVvR*h6mspMxT#!OB@!}{H;Z`Rs#>l+U;vhtYik8oj6GavHVc~)b(Cog{{yK6GDf0a8HeQlvIAJaq7=M z6+Q71qi@!4Enfd7DFoz1E#moN_E1xXno`tt?RG?cM6xDSt)>Cw#}hJ)!P#i%vBc_D zGtSEz($$bRfbozeH}pcy}1jXfaF2EoQ8dk9N1=C0^6Q7H7J zwmnw^j%^nN+e#H-Ap8|=uZzMJrIbFD$T?V-E}^(E{bo_<58r)P2*+w#uwPdVZ=pG? z?<$8CD?Xtg1*K~@^(K2Stod=Ch&)C;bsIpPQUG1B?^2KXu?}SOq~47UD8a2E;0&0n zNG1+;C8A$4%u%30I{`dK@^Q~OjupD!gk)x95S5L(P$2UdYXdYw?n`nLNb{btBsB7ukbEH+OdSK}b_q`Me! zpU8r-lA-=2`P=A+COOboqx#?|6YS9MH+8O1K+6e`j))3_bZvfFn0g6EzHbE5fz8zb z4%e*NNi{BfcOrpGv>~HX>#omG1>=j_a|klH#NR?6QzKE4kukE@Fc?d#W^FK;q5xed z_leo4-ihN5BfL@+Q?DTz1mB|h&$-$=F-uWr-B=l3iC%a^5dR9W1X1h8%0!|OP3jmp ziQvF#2)P)}D^X~44@J@#I>!4TkFMy?8GwNhl60mj;C9052% zYr&Dh`*g;bATW~3f-WEWzTVIfVVKf2CptJ7hk`Ae3k?7n6T9^;kYLa2Sgl%t@mvCX z5wx81En;Ms1F#HcY<~T6IBBRONqES<<}&d+F_RdIJ&eg8D{3L6Es8)xBBI?Cr8vw4 zuHY(e^TfFAO`o;n7I2~*v|>Y>P{^tQI4i-@<~ppnH&kgJ4s_nn5h%lfT7*gx_H}0F z5+yy*Rxg1jvld+~G)c-jFXI_RGK3hx?LML0OSWE&>o9zQxX|H_&C&!h1|lL(E?mzX zY}JpaW}1WXYgx5JW9OL&K((Wql|q zNX6@Uov5e?FPZnTLONhwN3`{{UcA5z#k|1o>n$kSWxy`h6bn!WAo8E*0cs;ehbN`_iSQ$BqqD%NioIQr$I( z3}`QwS*si6{uP(Z{&+r}M`9<^%bft0Ns>#cD3E3H4+8DlH6BoY1=ZG4a-f8gW}@~X zP}*=*Z8hsSXxj{Q^swpXMRW`d=Kka_gaL|lyaMHr`TKeAknOjkM|8ou;Y|Xp8*YPu z|NL0NvCx0MJ`#J{UZGjIF z-u56Ju6VH$tYnRwvoWSg0&egH+()-B%WOQU86$NJOf6Rt^ zRrL^Rk|m1Zip{VAW(;<3cE;Wr9+y*BUoRw-U+M%tC2#VQ88>SO@13aJtDnc5ICsJ7UJqqdUAI z9Y>8KG6?a?UJyq*tNIX*0Uj?=#AG?z+tKiq6{r^>T0r#^2g77@jts=35qhj|Kju~m z19Yq%`H@xYpSJvXUNlj@vwhfNk5`QU6r~aJ^`t(rwL_ zeBZB5>iSD2nQx3^mp1(S=OA5I+Ffwa$NEzngyK)We*h?02w;>9<_B!=MBRDe=w}2& zw@MIZzEZ(%x*@=B9Ta?e3W=XpT7#s#bhIbmB)H!S2j05)3GB+sa%ZDL)>wBN%z z=GsRO7ak9B(iN!dXDZp*Bz{4~M5{Qsm}EKQE;NPtf@36@q~zQfyB;c&TxHJ6cd2Rj zP;Wocy)TiV$;M{3;_WN5O+hu`4DvAt<8{vVAFmQTl~>lR}? zJ#a3PFY&R6bY06WKk_8IuGrVrZL|?cXr*bCl6rcK^UB7j__r%kBR94XusLhK(0D>> zcV4YehuQe`k_dpvDEP*98&B6u6S zbcBp2rOQ~n3+b%^P-PbLXpK!j>Fg@JOZALlo$S|VZufl*VOwC~17%kUi;MA--53d! zjYsIe8V|-sE`?qC)3f`pfUM3x ztkboG@n~AeM`1|zRm@Jms`05gppB4mdp;*;^H_b0`MH>G=oq;6V$Fo;$Y+frx7opI zhJp_(J&-Ooo6gV_j2!@eIyX#U@J@Jq;;+)u z2(jRF%zWv{6$`r$4`uL~%Go_1yQ^#-*AZEL2N`Sn_#Be1KT+0tsCAYt^AWx!pH*~x zhZqz4^vLR0Z_`T$$AhvR|0a2PBjn{Iv(tLVyx}-^=f(0^puyoOgN#7IhdHN??vR|o ztS+qlR%>f9C~pcrtjRC!iCDqG!=r>f(ivYzJ^na#>J9*NYTlq;)1PO3tiP~TVxt~|=+#;YubM48x$c&= zHqYd#hn)gSzFmO#`@oR<7%XRSj^Dts*E4@QD-;5sEdyzVjA79QuavFp4EEz*i)l3W;)rr^L`7F51R`#T`Lc@2 zQ(98%Sz zC>kK1XGagZ(TAhwXWq+2Vy@_kZg$8v3l~VZLL9KMxPp(@RpgPRrmBD})22TC`_rZO z4m|o=rYBF7K(Ub6Nds3gRK-jxPL>5!$Vb^eJ!UP*Jb;0V=f+oV=hMo8XDdf-?WH#q z^Gix5e8+ssq3gK#qyYhb2m{|M46P+U<8RKV{{jR{C^9Mc(KjiEww#pRA_-I>b%F21OO%a@at?4sn(dDq*O)kHu6}!Y~F<8mabSo3W4j zQRVNcy9(y}0yNnP!bPcQ(_1cWEu@ojBpQb5M-@!qkj&k&{`=gdx*MmDSDn(*#`Q!$ zZ!h?s2q8q(LfP`H=tkoS)D0b0z2S$>!1?2ZL`$+7eEJwN5X=&C^t;j1IbisG{9+ka zRrO+*{70@KAa}kyDn+WPd{U7BTE~|;=h4-R7X8AApv(#OA_e=jU&Ch^BdS#0*E>x# zpFjTiLsQ)zY4PFe7~ovsmQ+qnRD#POotq+wWdUyytT=ns{?a0i{+;nhU;b7kq!OCP z8-9WB+ZDq)FX|q@Lag)5JLp5U!_dS5u30bzUcR6d&Z2zc7 zj88*Keo`=AYHYGGpTZUw4%-)F)IYGDN@1ukQ()1psig&rTY_Vs02hOzBeNU&Su&U1 z>|0Mw6!64Bn_iY%fo(&?|LTW}3sJJbQpqdQ7}$B=p<|o3fQ^C)%yfVi)D^{EY(qiM zPu%lNU(99W?JzkfFB>dd5(zb$;cw9J4|OEDlY@yZH6Bj5Lzhazb;E?!Xkx5&P0@?|DY7d>L0bT=hDD=X7fx%q?Aq2i)8{R|H zlAW={BsFM|cp@F(25-asEhs(?MnA<7YAC|LONXIN!p$I^<(qYz4UyVMkc*~5^ z)fg8tx8-$7&>?B(SzQmKa#|MU@SPSbb zjxUsm9_In!`ws$-f$soyrBQh=#v_MuR-&P}Rc^>ch6xsr=4*%eV@cxE$y#*i+ zHGV)^u-*GcF3~b1#s9GoXH#MO`#>!zdr1 z?$E=-s*KsvmY$V`P=5!#{Lp)aiICeA8Pyr5JqK>NM1Q!_#m$ZUGl<=eXOQq{H|)Vc z$-eON15oPlefNJM-U6artp)n~XyL2jgHjvP^7HnEtAzws4wI!P`MH<1HHHS18gloq zPz3dMcSM=ZvNv@=?4H|d;d)au5qwa;Qw>tIB|TY-;RpAdM0~#sU?MZ1JxL8cM(7AC zFfd=Wv>e~@i!?JicV7SVcr5+oa;7c54xKNiW&#GiP4$LN)oF6oT{&UP!PY9;K4Vkf zG|;2t!JWQ%BOV_fZ(Hk%BybZkPKei9br<-sq9>He2KfQTRZSSfCb+QVOTjL3`SRsE zpmk8M$&()>1L_as!=Cn50g2zA>X=NIjA-^~8PN0s5_l<;WuPp{OTnlc1$_N{&-IOlFY(S_bGs-j<8jZaX$8Wx=BR6Ia z*Gjmh0jneuBP}HiZ3-In@gWJMvjd3$%^iUqLDJ+=h_#c5c6CJ~kmk_nDuKP^?3pul zkDWw-4851@3J?4i$RmkHAiEKAnMgD-Nons%a1?p&S57UP%)C3Dv%ipi@(B}h@lqpt zAKV=nZu)IpNGUcCq01GjN1P>iauCUV=8~j%yhD^Kq=;pY5LVomUb! zK-E%gQv#BQOo=48E7afz9BAM=4I2J9{PElo$x{IMX`eTI_i0GvH zOHWV#(F5*}O#qfNR<+eZ#tou;_;v0`d-Q)hGRvO?U{R^cCpF?vnK~T->$3A0!+2qLh*~h3hXc>G$(q-bRR0 z0jpLdFco4OqJUE`lwr`;!iDxi8T#R3&}<~?p@BzY#}(~l&!D4!qGS9W&5A$ZGQGTFb<193HU*K)VHgFlmgkB&$(^oj;rKkWwmKOP_V$%PHe#1 zbOJNJ6r>J~2rrl?5Wk_=?WY3720<)0GADZ1fctL@uLp8*gR^(QODST@yG?Z-mq)R|mZMxa;#-~B*rIX3(# zg_~pI%p-p6)rRY2_7#cUx(AW^viD;rld@* zab zA^B$(H?73x8K~#rOB!;~(^Hox%9PZFX2VEniCB(aZRRsnkz(NG6LAW48iUBLhjzL6 z*Q$`}n#Vqe7hx~VK#2Dp|8A=R`Hyd2A9XJmp8^1)q;nrka~4}nd^p6VHad#nBI$BU`1?5WeIulrgE)V}61wL9Nb zq|BSE*VU{MG@EX+9!wN!ZNp^i$0jGV>80%ZkDSUXo;2$Rj^$TKzJ+PLhS$T@bq~67 zWzc-WV4^FcQ*rZGzk$U@tzK_r1-PxZiDK4(tX;e+=GQ;S|F#O$S=#xDRl6lqgTsg(KH5s8$|Z*TS#<(9j4n2jX6kfFSdCaE5SDFgg8J9U6p0 zo8rg3C1fTA-RpUn4gduqhP`e3z6Lw2?QTussNhMxgiEeuB7__TjVO}Y5kXqhPE7Pz z;=NpJ)UKj^8`0AS5evlBwIznbYHiPg8c=Rwz!;oxH|4yOp-aRnGxa)9N=Pjj@&=40 zxW5uk1g%Ipm=I*qme^Fi?wQ!K&decknFXo|>@~i#v2c_3-2Do=1n8`j$e!{J(49&V zJE9$iY#RdLCv#+qB{oGMIx1ESOWTy;$iC_nwU~ln3^ve@5BFlFiUb|w7CQH#GD>ri zBBHwVFE;}tnd$^`?1wmPKUL?UZioYf0*Rcr6zkTg8!NP z*1xeyd3eP__0cSr8iLIdR*W8)MmE@08(Ph@FKG zQxlYfjCIy{B}g9lj6$;3L@{&+m`~`7g4pLLa@UUHh$~R~2fUnHod>f`pn;{Jxhb2x zT3S66HO)-fXXG-0L!c>mMuTI3I8^(cp|T;X?82s9G&rFzI~=QhUpIr_ZxT0FMx&_# z4{ZRcL9Ovuvp6V+&?qG6J3wLT^s_w@bxdS6q@Bubr{GBa%i0^Nfkuc*t|jC06A7K6e@Q>$Pewt zy}z8YFZ1w21J3f}R#)mk5>fuu15AzBBuB6kl@|uLfMj+B_=yo1yIj~>hN4cEVV>_* zQaXaC5_k7!hk29dRGo6}td8B`2@L+oWu&K>614;X4BL1&eWV7Z6r?s zKvI>RcAzKWx<-Ja#1*WFWKhf<{`!jGg@^=Nz0R49;GJ(44oWxe66ZD-gr1T?Xlksg zhqId>4nncq-UuZ*If}s%BGH^S);%;TMJgoPBESSjN~GQjy^p%2CPs~!+-rn)GHp6%ONsyH^-@yU(%^7gW5qB*gVzZg(G#DddXM#l+}>y?7&3U|@d6^5C-$I>(*5HOe1lbX63}5~YF_d>b10TP+}DSsm0&dn&LJQo zQv6-MwBv_3i<*9i1+{i5uyPALxICPnF$f|}NbYKOV3sVnY89!E4&Q_lA4+bK9pvMLOVDM!Q@c^N#07>yR3|6ZK{2#MV`ub~ETgbb}!M(&0ah(iq z%4uzOC8q_z5T1v=k=R;{D5PXE9~iq4K3s#m6=2R44JjXeGzK_amb-x z4Yh!d8LN>6Q5KXfqJv~Hxq`zQklr8!CX|FjHgg-dnXw~jc_nOuERyqDqiUOwOmKFf z_(|@sGQpO(BAJv4lg{AHW1#%iGO5P|#OqRHuEr5>oxDhTk=b(&lUC3Zk?w%2vhSLo zq2etCO(`0S4Q{*=vbxM}f-K>*wwX$5k{fLgl3t-mlyw4yGiHcJ zTnQH>3h-dqgEu#iufQsfg)We=tTy-+sG-bgvBO%4F*j=R=6OR@MB8Y$oOtNlhyQSbFk<>E#ChuQV-de zpKbvfw{h(ca;tjhncwSfdN=Z*%*6$npJ>$cE*|lb8UT&;@WgaS-n|*@XY4R!>JP{aV}V3qmOM74yDW+omt_E&6CHvg{^^Niw{slg-jPVy{F0HqiMmU* z-jaBIQKddtXT%aZ)Kb^}w+PxdwJ@1FnZKZ;>&3 z@1f^=369zn&OJAIr1MznIYjFWrw>_9>DZsfzmF#(>1|Hji{E~z2iWcb)N*yyXr5?6 zI(i2oDJile0Iv*^N19{@;-BgJXzAj_)$1bt%@S5bB(&a%Yrb}F|8Q;G?SjJ%4_?3s zl-_9F`<3ECu}U{~1M;>{M^PALlaCYAL+8MvL|J?jBIRBCJ!)#1sDJ~!3g*zbUl&S! z`2+r*KGw`>!1(U$3Q%VLCI^I;n87Nft%4>gu=v!dHUAyddHr%QE5Q;B^%Yu_czyMv zYgex>T(k&wA?yHxTp_Ss&j(_+9Rji*3iJD*c-lcPuZvipuZfmI@ts4RkE@XAi?*Ti zw|nvH6xq_g(>#`20L9D8r|(U9>mBA<;O6b^t-tjH-|<%$u;sPJ-LDe|1Xrzc(;05v z>sOxy5&rtlq@Mh+zmluX5?8?&ryf25hV3|W3w%zDz)UU7kd;~q(PhI(rr4nvwlfe~ zzT5?3+0~+>FK0MqhYj_%c&U@d!d&&cI5BOwWakYYOC8cW*(e%(Mld375p23|?*rj8 zOgmW$6AbvSeaFMSjWx7S!`&l2Ap&65hGCn^m+!E5R*=of2Du-g0=Ab=+Pfg3TlB1p zQs%ULskX2%xi`TdUM74X@-l_H+@VG&F7LSz@ zsy*{_^URaFGdac0G!24aUK3!Mv%)X__U#pTdfA=c{vUMS{et7u9gGSd4l5fFyk^7R z6)JldsJy|*bYIMf_UMxB{(0|TuXd7*as+93@uGU9zHTV1kMuQlKa6zfc$u#*tof;$US#^e{<2|yER?E2Ur&?`dy1XCzP;!x^!3C)1V=2ARr4WJEV4QZf@-C zyPy+a;WJdzB0r%N^gaQxkB!-eD!J%kUG<`mts#?k8QOn_HOft zbnLsUo^hfnz-qhNqZf@yAKvD6!v!bn-0BogWE`&ZN8e>xJQb@r(*GgQwrYjryT|i4 z%coyZ;G4(JjsazXJjgKFG2Q-#tK3MphaRNJ#KX5~Pn$Hu6_;F4<#OyJ_w}8=k|Wi1 zBkM2{BaetEV13d&`e-uagLWzKmbJbUPksM1Z}94AzMQZE#GSl^8#k70hkAD(>is#v zMTqKpKzO^e=cX>0Q#+_;r&U%%?EkyV3dPE0eD zB70O-^&2sHSGL)ytE)E-tvQh9?Bdc4ehF%-kh0(c%ybD5lN-?Hbj3YMUwE3wvrpYT z2OxaI45xP+d5+as_!U6(?kIfluRrY2Pu9hv$_RRZzI0)SR)nmS6Bx{#9$rkX@CT;Y zv4$&kq%X1oyK|f<2K$8>2HL27KuP6+Y$YQ$b-07ZH2g;; zEwF1&CKCMG);>ZoUo@Qe*?zM`@ib!?HfQ%=_8r!EdRK7UwFD9I(S3{{_wMN7!-p5u zC(m!~o5b*sjPGukKPnLf%SBp7>I*l{7~WZtf<8pivuB)`RfSW660uGcd}CjC4l_3SX+Z-zoelxOhSlu> z-JZn-$^3Z=F9!sVR!K;ly5SU^1^VzDG$6Bc;sBka40(2^Vn6&j%7@%t?6BQ!J^F@c z;cqUAU07=Gys)$yI1YGC541jubasU!Z!g2;ewDV}%Rk>W$&JoS?1<5T(nSHGA_T>n zfASlQ!G@S>vva`BWZWeieZ%DD@71zVtvS&auHNL-rcrk`Cnu*F*r+w!u~C}yVwwdv zV5Xdz!e_T&|Ni}p7U?3h6^6vq4vzV{sN^skUGwhGu*8s0HRlGQKRXn(YFNGMa#K7EoiOW$)B;oC56+3LR1h>HjzdE2 z&|Gn-txT+3K}*j;NoTEi<_s?GhAHmbq=?VIA#oRzrB}H5&FaLYp_-9hMFYQ&b~9{V z&TFWus$ST7Lxf;kFL&zyr)3?1`+v88-G`-oSVnL&=jsBa@zX<_$c2_T*j)1H3{_Q7 zHj`i3_S~jI%K`@UPS;K3lq&TV`myUl7S?$_zMGkEVKwJo)x*}FpUtiyEKp6X*3cb&@w|qZrdiXHCJ`)ER;xsxU zZ22l1SMXS|xrM(q&OKxDT+N@@-^?zAyM%DdmcS~%8QL5+Zhe00FNqac=)IgtDl!VW z{exOY>Et)Vg(o)!UyB<`8c|dro}shQGViUMa2d+A_YQw$ab@uq1$ph_h4_oq4Ro`! zXw1$EewUs#vLB)oVdxw%jxy8&u_bO7O;lYRgz}*9!@KJ{;}%mIWMpjoKF#WfAAV?v zYj8ieO(>qzHlOQ)m>}bf5ERX}-7ZG5hO`FEf zejh#d3>fjuM`OdC7DWqWPMaMaN2WZF5|z%SoIu6){b{)m3mtny=2MRA}Qj%*wG#RU*ny1HTDY$S^&^72@&79()+ zq6$%#;tH$*ZnJpNHTc%wZ;MD=I(`%@SiCjXhdc9R=jN(d$IpgfJ<+=8^|xrJyCdW; z69wimtf+8pmhq7zuA!l!YD4G@o^x?Qq*=mCTbMg7MTwEkxo!{u`PHfu^%Wi(o zT3xUZN@eqQ#! zsJPhE4|}+ey@YkB3lt9fh%Mt~j!Jr=vyd6;dd3(8P$7Q&1LbqP$7+OIe6@ZtF8E3# z0MC_8urB_JP4@f)2ItIq-5srR9((1J(wQ|6_%myg_**3CtqPYQhuVb9T=khG*pDjLN?3C-88H`vU(kvzyai00_Gp3~X7eob(o-Fh7vlO5Cj!S}~T zmW=clJY;7d9d+utcFlZO@qK#BE4eL*I7uiry0Pg(1~%hirJ02{V5FviYA)dDU*C6A z;6o2s7p2XvJ+JEGV|qB%} zCJ_-U&$Q;$U)N(?r^27FFJ82|<>NkG=QqAroG>(FP$ui z$Z~DRu06e~%z37`<;HIHHedk0YYzUx991!r=sunUKABF10(N)Z4{6{JMTLc3S356k zzv-=I_6W_d5CC3^S!@dqa-U^f)PzO)O!=1S9xZhW_PY|p)R_Z;->89kZ`Qq`VT}aH z{BFT&AtCq2)7P(@F8l69GdfNb*AdCQ9J#|0LifcSQD@XEGM2@H-0fVUf6WZ9wiSx6VFvj&4G?Ze)r1Z!L? zyZ_+9gSc0o+jPOE(n!ie+nEd9e^+GQ=Jx@V(R8DcV_T{3^HeqqcQcEYG$;zg-O)SB zz(z4OS968r+C>>t%6Z!7<)QUL1zwvyEB8k)7lrps5#?dz2`AlGs)A~8*DmaU?)t3N zKD4Ce-BWYNS>mdyCME3rGnDjqnY{bfD-}ue$dL%%&BSN@{pI zVC}7x(;gxuWrkzd@3+FkpH>CKR1s@Zkj(n&@Mg>#ftSln=pbN*>~aEMXeiA9xs6f$#VsN+{aIP!Q{4Gpt=28vb4bj35su zS`jNiW%Vei&!XqRsOD!#cWnqjdFaISF-sR+U}Rokqf*KsFTWN89WJm*Pbvm)6)+jR&m%tx zuyjfI%~85!ht(m>g(oy3Wix+>hW1bf4El9pL;-U|N_Mg54kwB;2f`FlZq>sd!j(!) zni%W!N6I1{y~T8n)!JBeJ(^!xKW0hpG2qx5oyAzg!Nj&GJwg zqzh;9%$gz7yOV}VlpK6p))_h?5Q?^qekd5t6G*7p2k#a=+#SZqMAD`QYJ_@LYX8GX zy2}kzI}%iB2K#rV+j{cy!Lkk_ZFKk%=vIX7&Lz~iGoh{CDtJhfN>;#%u^BZ`>g0ua zV4tXW>C0sAS6aOxR2YU1_XK%_(h?1OmFy<}FRQKxh$|1-{HR83h1e9`$ z9o|#!h@4Op>_K7c=E68++v|o#);X3FYxoHDounMQ&*>a$)N_|K@M?meQ-37i!ac1kh+L_A{#XVBV>SPFcZB-z!cIQU1YtjLl?!U4a$&^e32zjmqkmXA@1%wT)UAs0N^N|OL zkuWZh+6^D>;Ob}3;+|BH&ujyR@6)Pk%?r(qG|8eyDhnJhsqQ1sfdDz?P9e`WjO7wq zsR#58v=WmfLxuKvTl3T5H+v$Tr!|jqkeYj>$~@544)ETu51rZpwX8Sz9VO_QgoFe; z*Z}P3#X|AcBYXr%+F6r`_@$ygn2LbQ{6(-r>X-Wqyr{W|`zIHb0uZPHqv0GtjY!o; z4d`Un19o&FU$k=rf&j*-mW3dJ2WyZ`Z)=&LU>%_8TEf7R-Z%RD4Z}`1ZxFk|x`IHh zUu%V{M}VJN!Eu!(*SUv^eNs4pAs9W>QBj$NoV^Hq!Mx|0n|$LPm$D@pen_*hs4l9= z!0pxRHcs`JaEITb-jiZw!n~E*v*s6EW?*R#-YtGUjV=m(Etp5B&yRf45~8rqlvdds zk73R!tScY7pc{4}F2F0ZXl-#7&Ck{p#Y!vDdMo5XC&YP!SdZPdtxjI67JlN5&-VjB zi#twRb7m~ew{xmNWwz*B;cv4`uE5!EV37#KGaAl42DFoA2&C5!{~78=v?GZ z19Q$vP&lzeeAL4dcppWSAuqr$ts4B*0>m&|O!{-&Z@m&$Y;16^lj$05~pc?(ksCQCBn&B#0}a`n43uQ*m!| z1VwucWqbVcx<`jnN6)!rKVgLfw|`h6t#L21FOOnr2E+hD5}~Bo7gzVURjj5WnVr#| zpu24KR6M2IjqoN-Q#jHdIvN#FjM z-A)E)?2NoKPaAwP9(YW_u>c6He7gPX8*;-fJEHQ>xkw;!7i~jE@v5DbBxZh|{=(?M zFu-DSxu%XzUF*BKsqZqvl96%mgV7B2O4RlJxM>7zmuk6AEwTE&se`H%=OuIPk^C8y zb8qF7%SLMU;CUSgWb@$*RX!&YS~F$B_mE6+BXvlnR8W2L1+3DvJY*mT(8EGT%?8{& zj~|5M79jf)Y3l2&T_2SXe9YXj$KL{Ab0&}h(IGHi&S7B}K_%kKB_@EfrW!{QSj@ZS+UCs$v~j3)T*iLTgX;JJ$*Oyyd)b?-Lw027^W=Rs*W z;n9KH896g@t9Q@df{4sm*7re_qlva0+Bm(xR$%Kt*_6Su$V_x@y(-(lbv%jj>JDy! z0+{h(8nHsyPir0$O3J_~M2R-Rc{IZU!8;S}^ZHZVW-e3)Xm~#amVs3}g_;+Z{Ua6O z8gmgCW01zpa4^Lp8G282FDnCFw{r~i{W#X0^RHt9(I`TC;R-{cENbh5d7_w=bDK^m zAAx5ddi|$U`V;Gh&8S!4_Z~}`D`ns~`dI|WQw_Li5!i;PZ}=>L-(^srhoH{R+QenU z<<+m5qs@5?w%Pp=X)5dv!zE%sfyg^!T0daLV6;%s&bbgAoLbp~dpipygab7IT4Lj# zN;8+l1irDV_c{~&idlt<1S_GZJKe<%Q@UYpn~sH@4+N$|(lBhq^qk4gPC;PObcqGH zd#C{x$zp0O<1G{Ys`)@GR~uFGp{p-*-;kM-ZmOW;!&qRQZ9|x!aK()cma|7=-jvm66EX+$ zq#X;NampRlWY+vuo5pfbsf4#F!8w;_Gy0VqM9>S0ks$aat$b(ev&)7Iup;fW>Uk#Q z=IkDk@IV3sG{jat>KX$N+p<%@n+0mHr5Tiotvti&YfW&DbN>YvQ0JWg%LIrBf5IO> zM{gL9vRQ8V942BuF3GwW%zur4cMf3n6viK6{<0yTg|>h8_yR*#+d7q@wPEfQ%ij&1083={V-pnCB^P&c0P_tEgCY%BcO`o|%>1S3+CY1o>^H#)4 z_bUCTObF=!x98Ryo~+X4{H}_7{JIBq^JM{2HGR z=NErkZ?Ul*S-jQ0ZwCW>+@L$LRN=Jh@#M71#s58?%(1t@M^#%pNNfiNBon}7W1H77 z#Z8&{;>1r8V?8tNnGbXQMXhY~75D6&vy7Kv7?f}wfeA%1<=t^W6TMiF>`&;3?W(fc zP%KObT&4Q)vb&vQz>3a?AVrpIVR;m&b1?3i)~>M&%{tGqAOACk^&SlCv5APr@L;{9 z@X>M?iJnJ@)HKuzBgU^cE>gKB8f47tTDauK?%T^z(BNXxkN?vV9dz>T6J{`DV>?tT zwz9IG+$IYBX7e-mBD)fD7bBVmwpMu<(=cErux@6cXI{%f*|%OyGn_(oX3QD8#`wuv z!zVmKziycRIo8K~!g!GV?D?O>8t3g-lai#z?lV2}j|q|Gir3Z~yO~py`6kVttV=i{ zaI@aN+ND@~m?K1b_Vv?li%XWJWNNlMO;qku`!K6TGAcmNmb;>C;Uuvxl>1rB)y z6DL8J=^(vN%eltQ^70I#fb_D@`y{Hpt5_+;PwX(F*v9OcuoeD^^x|I}++z`&&SZo9 zt7WX25Vdn`FH*)S;@g$ayO@kWIP*+M7ssB8pZ{TIG2yxN>nDSL!r%UBft6INy6eOY zBs0g8KQt_}N+;jdjXdL#g#?11(H>??2t(_8jfs$;Ad_5y1Iq+p!JG#FV&1U=bORJa z7kzwU1yY%Kt&us!XRM$M)&`^7n#j5i4Aw?ivkJ$t;0ifruW7>RoXOrHHQ}Ehe+@i6 zVOoEDnCl_@$rA{b_BxfLHRloJU!+s(>I2jh>7Z&LR@j1@I=m;?Re-`M{3)1(*uQ#7T=|4Zcxy#e6d ztS{uvz6l3Z+X2~p%$u1nV5@dU1^A$``}fSHtg!IvMoEkHIJ<+%{}TKxV`jd1%RVal zqJwKMt-p zHzzLFY)ABfis+%78`WMNQR}wOBP4H@$dWsV+mN`pG-Jo{7-CbUntdBJfI0rWc>Qyy`p7bKnXfA z2WE?5A0rO2Ef8j1Jj(-lPq*pjt-}9LF$X7Hkt379Ldm|IHG|8abp^dX;R2(TGh1bD zSl*`zF7vDoaPO?xS9j+f@iwnS<+9fH4GIIV9#q6K;33x8Yxyv)+7-e7``Hp5!GB_Y zr)3@Z-xX%Z>vk~LDb|TS%PbWu&jQV6G+Fy^`6;+Ha}1yU>!EO7Sc;<&b46zje%7bZ zk1cz~iraq|ig2I9ERT)tTMtF~pZENCBwpwB{%IVx{-%1rB0Wp*@8d9Y29J--mxdwb zx4s%9T9)b)Q8Cu_iTUa?ykPuNCA4AaUJlmmKf&y#GGE|q`p3U(Jj%L$vPR?cVoX_c z5DY=rB-T>KI&iil)n8b_{sD0NZ(XPX&k783=>KN|WT$K-E8pB?n##zg#?AtH)s##B zGZM{@hxAYQ!YhJoSy&zmqIBl@WQCp>`Kz6~CuQjud})D3+(qx+$wyOd?>Kc4J8OCq_ zwd&^MoBd~sO?uTogimLWM_(?EvhSajMF9KwfowC?7;x*;;eiv~0ZcSDnwzz55-~79 z^{C|i2mBMS$A-VwacG(C1dht8`a{P!LV=HJoBBL(^+CG6)gjIVyvIMRIqm;0LN)%u z|EcuSnMbgrjD=sG`FnHXydEm++#mu|nVN`+VCsZVv|dNj0UH`+3=aKxCO+Xg^lP@? zSPuQ!9}18E2OW^lp8wx4bQtgW4_VMZue>92i+?KL!};4$SG;bC^`?E+hYninOX*qa z;|H6_8WGV|Ys6OxOY9LAmcoBxYq5Ec)S5NwvmbbT@&Z%yL&gTjzx;ybTc(QQ1Cw+$BO=16.14" } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz", - "integrity": "sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", + "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", + "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", "dependencies": { - "@algolia/autocomplete-shared": "1.7.4" + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.4.tgz", - "integrity": "sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", + "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", "dependencies": { - "@algolia/autocomplete-shared": "1.7.4" + "@algolia/autocomplete-shared": "1.9.3" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -51,79 +63,83 @@ } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.4.tgz", - "integrity": "sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg==" + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", + "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } }, "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.3.tgz", - "integrity": "sha512-hWH1yCxgG3+R/xZIscmUrWAIBnmBFHH5j30fY/+aPkEZWt90wYILfAHIOZ1/Wxhho5SkPfwFmT7ooX2d9JeQBw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.18.0.tgz", + "integrity": "sha512-rUAs49NLlO8LVLgGzM4cLkw8NJLKguQLgvFmBEe3DyzlinoqxzQMHfKZs6TSq4LZfw/z8qHvRo8NcTAAUJQLcw==", "dependencies": { - "@algolia/cache-common": "4.14.3" + "@algolia/cache-common": "4.18.0" } }, "node_modules/@algolia/cache-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.14.3.tgz", - "integrity": "sha512-oZJofOoD9FQOwiGTzyRnmzvh3ZP8WVTNPBLH5xU5JNF7drDbRT0ocVT0h/xB2rPHYzOeXRrLaQQBwRT/CKom0Q==" + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.18.0.tgz", + "integrity": "sha512-BmxsicMR4doGbeEXQu8yqiGmiyvpNvejYJtQ7rvzttEAMxOPoWEHrWyzBQw4x7LrBY9pMrgv4ZlUaF8PGzewHg==" }, "node_modules/@algolia/cache-in-memory": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.14.3.tgz", - "integrity": "sha512-ES0hHQnzWjeioLQf5Nq+x1AWdZJ50znNPSH3puB/Y4Xsg4Av1bvLmTJe7SY2uqONaeMTvL0OaVcoVtQgJVw0vg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.18.0.tgz", + "integrity": "sha512-evD4dA1nd5HbFdufBxLqlJoob7E2ozlqJZuV3YlirNx5Na4q1LckIuzjNYZs2ddLzuTc/Xd5O3Ibf7OwPskHxw==", "dependencies": { - "@algolia/cache-common": "4.14.3" + "@algolia/cache-common": "4.18.0" } }, "node_modules/@algolia/client-account": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.14.3.tgz", - "integrity": "sha512-PBcPb0+f5Xbh5UfLZNx2Ow589OdP8WYjB4CnvupfYBrl9JyC1sdH4jcq/ri8osO/mCZYjZrQsKAPIqW/gQmizQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.18.0.tgz", + "integrity": "sha512-XsDnlROr3+Z1yjxBJjUMfMazi1V155kVdte6496atvBgOEtwCzTs3A+qdhfsAnGUvaYfBrBkL0ThnhMIBCGcew==", "dependencies": { - "@algolia/client-common": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.18.0", + "@algolia/client-search": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "node_modules/@algolia/client-analytics": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.14.3.tgz", - "integrity": "sha512-eAwQq0Hb/aauv9NhCH5Dp3Nm29oFx28sayFN2fdOWemwSeJHIl7TmcsxVlRsO50fsD8CtPcDhtGeD3AIFLNvqw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.18.0.tgz", + "integrity": "sha512-chEUSN4ReqU7uRQ1C8kDm0EiPE+eJeAXiWcBwLhEynfNuTfawN9P93rSZktj7gmExz0C8XmkbBU19IQ05wCNrQ==", "dependencies": { - "@algolia/client-common": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.18.0", + "@algolia/client-search": "4.18.0", + "@algolia/requester-common": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "node_modules/@algolia/client-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.14.3.tgz", - "integrity": "sha512-jkPPDZdi63IK64Yg4WccdCsAP4pHxSkr4usplkUZM5C1l1oEpZXsy2c579LQ0rvwCs5JFmwfNG4ahOszidfWPw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.18.0.tgz", + "integrity": "sha512-7N+soJFP4wn8tjTr3MSUT/U+4xVXbz4jmeRfWfVAzdAbxLAQbHa0o/POSdTvQ8/02DjCLelloZ1bb4ZFVKg7Wg==", "dependencies": { - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/requester-common": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "node_modules/@algolia/client-personalization": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.14.3.tgz", - "integrity": "sha512-UCX1MtkVNgaOL9f0e22x6tC9e2H3unZQlSUdnVaSKpZ+hdSChXGaRjp2UIT7pxmPqNCyv51F597KEX5WT60jNg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.18.0.tgz", + "integrity": "sha512-+PeCjODbxtamHcPl+couXMeHEefpUpr7IHftj4Y4Nia1hj8gGq4VlIcqhToAw8YjLeCTfOR7r7xtj3pJcYdP8A==", "dependencies": { - "@algolia/client-common": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.18.0", + "@algolia/requester-common": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "node_modules/@algolia/client-search": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.14.3.tgz", - "integrity": "sha512-I2U7xBx5OPFdPLA8AXKUPPxGY3HDxZ4r7+mlZ8ZpLbI8/ri6fnu6B4z3wcL7sgHhDYMwnAE8Xr0AB0h3Hnkp4A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.18.0.tgz", + "integrity": "sha512-F9xzQXTjm6UuZtnsLIew6KSraXQ0AzS/Ee+OD+mQbtcA/K1sg89tqb8TkwjtiYZ0oij13u3EapB3gPZwm+1Y6g==", "dependencies": { - "@algolia/client-common": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.18.0", + "@algolia/requester-common": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "node_modules/@algolia/events": { @@ -132,47 +148,47 @@ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, "node_modules/@algolia/logger-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.14.3.tgz", - "integrity": "sha512-kUEAZaBt/J3RjYi8MEBT2QEexJR2kAE2mtLmezsmqMQZTV502TkHCxYzTwY2dE7OKcUTxi4OFlMuS4GId9CWPw==" + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.18.0.tgz", + "integrity": "sha512-46etYgSlkoKepkMSyaoriSn2JDgcrpc/nkOgou/lm0y17GuMl9oYZxwKKTSviLKI5Irk9nSKGwnBTQYwXOYdRg==" }, "node_modules/@algolia/logger-console": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.14.3.tgz", - "integrity": "sha512-ZWqAlUITktiMN2EiFpQIFCJS10N96A++yrexqC2Z+3hgF/JcKrOxOdT4nSCQoEPvU4Ki9QKbpzbebRDemZt/hw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.18.0.tgz", + "integrity": "sha512-3P3VUYMl9CyJbi/UU1uUNlf6Z8N2ltW3Oqhq/nR7vH0CjWv32YROq3iGWGxB2xt3aXobdUPXs6P0tHSKRmNA6g==", "dependencies": { - "@algolia/logger-common": "4.14.3" + "@algolia/logger-common": "4.18.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.3.tgz", - "integrity": "sha512-AZeg2T08WLUPvDncl2XLX2O67W5wIO8MNaT7z5ii5LgBTuk/rU4CikTjCe2xsUleIZeFl++QrPAi4Bdxws6r/Q==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.18.0.tgz", + "integrity": "sha512-/AcWHOBub2U4TE/bPi4Gz1XfuLK6/7dj4HJG+Z2SfQoS1RjNLshZclU3OoKIkFp8D2NC7+BNsPvr9cPLyW8nyQ==", "dependencies": { - "@algolia/requester-common": "4.14.3" + "@algolia/requester-common": "4.18.0" } }, "node_modules/@algolia/requester-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.14.3.tgz", - "integrity": "sha512-RrRzqNyKFDP7IkTuV3XvYGF9cDPn9h6qEDl595lXva3YUk9YSS8+MGZnnkOMHvjkrSCKfoLeLbm/T4tmoIeclw==" + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.18.0.tgz", + "integrity": "sha512-xlT8R1qYNRBCi1IYLsx7uhftzdfsLPDGudeQs+xvYB4sQ3ya7+ppolB/8m/a4F2gCkEO6oxpp5AGemM7kD27jA==" }, "node_modules/@algolia/requester-node-http": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.14.3.tgz", - "integrity": "sha512-O5wnPxtDRPuW2U0EaOz9rMMWdlhwP0J0eSL1Z7TtXF8xnUeeUyNJrdhV5uy2CAp6RbhM1VuC3sOJcIR6Av+vbA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.18.0.tgz", + "integrity": "sha512-TGfwj9aeTVgOUhn5XrqBhwUhUUDnGIKlI0kCBMdR58XfXcfdwomka+CPIgThRbfYw04oQr31A6/95ZH2QVJ9UQ==", "dependencies": { - "@algolia/requester-common": "4.14.3" + "@algolia/requester-common": "4.18.0" } }, "node_modules/@algolia/transporter": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.14.3.tgz", - "integrity": "sha512-2qlKlKsnGJ008exFRb5RTeTOqhLZj0bkMCMVskxoqWejs2Q2QtWmsiH98hDfpw0fmnyhzHEt0Z7lqxBYp8bW2w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.18.0.tgz", + "integrity": "sha512-xbw3YRUGtXQNG1geYFEDDuFLZt4Z8YNKbamHPkzr3rWc6qp4/BqEeXcI2u/P/oMq2yxtXgMxrCxOPA8lyIe5jw==", "dependencies": { - "@algolia/cache-common": "4.14.3", - "@algolia/logger-common": "4.14.3", - "@algolia/requester-common": "4.14.3" + "@algolia/cache-common": "4.18.0", + "@algolia/logger-common": "4.18.0", + "@algolia/requester-common": "4.18.0" } }, "node_modules/@ampproject/remapping": { @@ -1985,18 +2001,18 @@ } }, "node_modules/@docsearch/css": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.3.3.tgz", - "integrity": "sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg==" + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.1.tgz", + "integrity": "sha512-2Pu9HDg/uP/IT10rbQ+4OrTQuxIWdKVUEdcw9/w7kZJv9NeHS6skJx1xuRiFyoGKwAzcHXnLp7csE99sj+O1YA==" }, "node_modules/@docsearch/react": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.3.3.tgz", - "integrity": "sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.1.tgz", + "integrity": "sha512-t5mEODdLzZq4PTFAm/dvqcvZFdPDMdfPE5rJS5SC8OUq9mPzxEy6b+9THIqNM9P0ocCb4UC5jqBrxKclnuIbzQ==", "dependencies": { - "@algolia/autocomplete-core": "1.7.4", - "@algolia/autocomplete-preset-algolia": "1.7.4", - "@docsearch/css": "3.3.3", + "@algolia/autocomplete-core": "1.9.3", + "@algolia/autocomplete-preset-algolia": "1.9.3", + "@docsearch/css": "3.5.1", "algoliasearch": "^4.0.0" }, "peerDependencies": { @@ -2017,9 +2033,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.3.1.tgz", - "integrity": "sha512-0Jd4jtizqnRAr7svWaBbbrCCN8mzBNd2xFLoT/IM7bGfFie5y58oz97KzXliwiLY3zWjqMXjQcuP1a5VgCv2JA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.1.tgz", + "integrity": "sha512-SNsY7PshK3Ri7vtsLXVeAJGS50nJN3RgF836zkyUfAD01Fq+sAk5EwWgLw+nnm5KVNGDu7PRR2kRGDsWvqpo0g==", "dependencies": { "@babel/core": "^7.18.6", "@babel/generator": "^7.18.7", @@ -2031,13 +2047,13 @@ "@babel/runtime": "^7.18.6", "@babel/runtime-corejs3": "^7.18.6", "@babel/traverse": "^7.18.8", - "@docusaurus/cssnano-preset": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", + "@docusaurus/cssnano-preset": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "@slorber/static-site-generator-webpack-plugin": "^4.0.7", "@svgr/webpack": "^6.2.1", "autoprefixer": "^10.4.7", @@ -2105,9 +2121,9 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.3.1.tgz", - "integrity": "sha512-7mIhAROES6CY1GmCjR4CZkUfjTL6B3u6rKHK0ChQl2d1IevYXq/k/vFgvOrJfcKxiObpMnE9+X6R2Wt1KqxC6w==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.1.tgz", + "integrity": "sha512-ka+vqXwtcW1NbXxWsh6yA1Ckii1klY9E53cJ4O9J09nkMBgrNX3iEFED1fWdv8wf4mJjvGi5RLZ2p9hJNjsLyQ==", "dependencies": { "cssnano-preset-advanced": "^5.3.8", "postcss": "^8.4.14", @@ -2119,9 +2135,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.3.1.tgz", - "integrity": "sha512-2lAV/olKKVr9qJhfHFCaqBIl8FgYjbUFwgUnX76+cULwQYss+42ZQ3grHGFvI0ocN2X55WcYe64ellQXz7suqg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.4.1.tgz", + "integrity": "sha512-5h5ysIIWYIDHyTVd8BjheZmQZmEgWDR54aQ1BX9pjFfpyzFo5puKXKYrYJXbjEHGyVhEzmB9UXwbxGfaZhOjcg==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.4.0" @@ -2131,14 +2147,14 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.3.1.tgz", - "integrity": "sha512-Gzga7OsxQRpt3392K9lv/bW4jGppdLFJh3luKRknCKSAaZrmVkOQv2gvCn8LAOSZ3uRg5No7AgYs/vpL8K94lA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.1.tgz", + "integrity": "sha512-4KhUhEavteIAmbBj7LVFnrVYDiU51H5YWW1zY6SmBSte/YLhDutztLTBE0PQl1Grux1jzUJeaSvAzHpTn6JJDQ==", "dependencies": { "@babel/parser": "^7.18.8", "@babel/traverse": "^7.18.8", - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", "@mdx-js/mdx": "^1.6.22", "escape-html": "^1.0.3", "file-loader": "^6.2.0", @@ -2162,12 +2178,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.3.1.tgz", - "integrity": "sha512-6KkxfAVOJqIUynTRb/tphYCl+co3cP0PlHiMDbi+SzmYxMdgIrwYqH9yAnGSDoN6Jk2ZE/JY/Azs/8LPgKP48A==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.1.tgz", + "integrity": "sha512-gLBuIFM8Dp2XOCWffUDSjtxY7jQgKvYujt7Mx5s4FCTfoL5dN1EVbnrn+O2Wvh8b0a77D57qoIDY7ghgmatR1A==", "dependencies": { "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "2.3.1", + "@docusaurus/types": "2.4.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2181,15 +2197,15 @@ } }, "node_modules/@docusaurus/plugin-client-redirects": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.3.1.tgz", - "integrity": "sha512-Ye0z36/L8685ni0DIxHqPPaHIXFXiSF90QYiYfpODBX6NxvvveUTyylsDBU1GQhPXPn1bd39QgaOuZ+j9gfaog==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.4.1.tgz", + "integrity": "sha512-tp0j16gaLIJ4p+IR0P6KDOFsTOGGMY54MNPnmM61Vaqqt5omLqsuKUO8UlCGU1oW/4EIQOhXYy99XYY5MjE+7A==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "eta": "^2.0.0", "fs-extra": "^10.1.0", "lodash": "^4.17.21", @@ -2204,17 +2220,17 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.3.1.tgz", - "integrity": "sha512-f5LjqX+9WkiLyGiQ41x/KGSJ/9bOjSD8lsVhPvYeUYHCtYpuiDKfhZE07O4EqpHkBx4NQdtQDbp+aptgHSTuiw==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.1.tgz", + "integrity": "sha512-E2i7Knz5YIbE1XELI6RlTnZnGgS52cUO4BlCiCUCvQHbR+s1xeIWz4C6BtaVnlug0Ccz7nFSksfwDpVlkujg5Q==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^10.1.0", @@ -2234,17 +2250,17 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.3.1.tgz", - "integrity": "sha512-DxztTOBEruv7qFxqUtbsqXeNcHqcVEIEe+NQoI1oi2DBmKBhW/o0MIal8lt+9gvmpx3oYtlwmLOOGepxZgJGkw==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.1.tgz", + "integrity": "sha512-Lo7lSIcpswa2Kv4HEeUcGYqaasMUQNpjTXpV0N8G6jXgZaQurqp7E8NGYeGbDXnb48czmHWbzDL4S3+BbK0VzA==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "@types/react-router-config": "^5.0.6", "combine-promises": "^1.1.0", "fs-extra": "^10.1.0", @@ -2264,15 +2280,15 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.3.1.tgz", - "integrity": "sha512-E80UL6hvKm5VVw8Ka8YaVDtO6kWWDVUK4fffGvkpQ/AJQDOg99LwOXKujPoICC22nUFTsZ2Hp70XvpezCsFQaA==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.1.tgz", + "integrity": "sha512-/UjuH/76KLaUlL+o1OvyORynv6FURzjurSjvn2lbWTFc4tpYY2qLYTlKpTCBVPhlLUQsfyFnshEJDLmPneq2oA==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "fs-extra": "^10.1.0", "tslib": "^2.4.0", "webpack": "^5.73.0" @@ -2286,13 +2302,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.3.1.tgz", - "integrity": "sha512-Ujpml1Ppg4geB/2hyu2diWnO49az9U2bxM9Shen7b6qVcyFisNJTkVG2ocvLC7wM1efTJcUhBO6zAku2vKJGMw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.4.1.tgz", + "integrity": "sha512-7Yu9UPzRShlrH/G8btOpR0e6INFZr0EegWplMjOqelIwAcx3PKyR8mgPTxGTxcqiYj6hxSCRN0D8R7YrzImwNA==", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", "fs-extra": "^10.1.0", "react-json-view": "^1.21.3", "tslib": "^2.4.0" @@ -2306,13 +2322,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.3.1.tgz", - "integrity": "sha512-OHip0GQxKOFU8n7gkt3TM4HOYTXPCFDjqKbMClDD3KaDnyTuMp/Zvd9HSr770lLEscgPWIvzhJByRAClqsUWiQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.1.tgz", + "integrity": "sha512-dyZJdJiCoL+rcfnm0RPkLt/o732HvLiEwmtoNzOoz9MSZz117UH2J6U2vUDtzUzwtFLIf32KkeyzisbwUCgcaQ==", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "tslib": "^2.4.0" }, "engines": { @@ -2324,13 +2340,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.3.1.tgz", - "integrity": "sha512-uXtDhfu4+Hm+oqWUySr3DNI5cWC/rmP6XJyAk83Heor3dFjZqDwCbkX8yWPywkRiWev3Dk/rVF8lEn0vIGVocA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.1.tgz", + "integrity": "sha512-mKIefK+2kGTQBYvloNEKtDmnRD7bxHLsBcxgnbt4oZwzi2nxCGjPX6+9SQO2KCN5HZbNrYmGo5GJfMgoRvy6uA==", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "tslib": "^2.4.0" }, "engines": { @@ -2342,13 +2358,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.3.1.tgz", - "integrity": "sha512-Ww2BPEYSqg8q8tJdLYPFFM3FMDBCVhEM4UUqKzJaiRMx3NEoly3qqDRAoRDGdIhlC//Rf0iJV9cWAoq2m6k3sw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.1.tgz", + "integrity": "sha512-Zg4Ii9CMOLfpeV2nG74lVTWNtisFaH9QNtEw48R5QE1KIwDBdTVaiSA18G1EujZjrzJJzXN79VhINSbOJO/r3g==", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "tslib": "^2.4.0" }, "engines": { @@ -2360,16 +2376,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.3.1.tgz", - "integrity": "sha512-8Yxile/v6QGYV9vgFiYL+8d2N4z4Er3pSHsrD08c5XI8bUXxTppMwjarDUTH/TRTfgAWotRbhJ6WZLyajLpozA==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.1.tgz", + "integrity": "sha512-lZx+ijt/+atQ3FVE8FOHV/+X3kuok688OydDXrqKRJyXBJZKgGjA2Qa8RjQ4f27V2woaXhtnyrdPop/+OjVMRg==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "fs-extra": "^10.1.0", "sitemap": "^7.1.1", "tslib": "^2.4.0" @@ -2383,23 +2399,23 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.3.1.tgz", - "integrity": "sha512-OQ5W0AHyfdUk0IldwJ3BlnZ1EqoJuu2L2BMhqLbqwNWdkmzmSUvlFLH1Pe7CZSQgB2YUUC/DnmjbPKk/qQD0lQ==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/plugin-debug": "2.3.1", - "@docusaurus/plugin-google-analytics": "2.3.1", - "@docusaurus/plugin-google-gtag": "2.3.1", - "@docusaurus/plugin-google-tag-manager": "2.3.1", - "@docusaurus/plugin-sitemap": "2.3.1", - "@docusaurus/theme-classic": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-search-algolia": "2.3.1", - "@docusaurus/types": "2.3.1" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.4.1.tgz", + "integrity": "sha512-P4//+I4zDqQJ+UDgoFrjIFaQ1MeS9UD1cvxVQaI6O7iBmiHQm0MGROP1TbE7HlxlDPXFJjZUK3x3cAoK63smGQ==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/plugin-debug": "2.4.1", + "@docusaurus/plugin-google-analytics": "2.4.1", + "@docusaurus/plugin-google-gtag": "2.4.1", + "@docusaurus/plugin-google-tag-manager": "2.4.1", + "@docusaurus/plugin-sitemap": "2.4.1", + "@docusaurus/theme-classic": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-search-algolia": "2.4.1", + "@docusaurus/types": "2.4.1" }, "engines": { "node": ">=16.14" @@ -2422,26 +2438,26 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.3.1.tgz", - "integrity": "sha512-SelSIDvyttb7ZYHj8vEUhqykhAqfOPKk+uP0z85jH72IMC58e7O8DIlcAeBv+CWsLbNIl9/Hcg71X0jazuxJug==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.4.1.tgz", + "integrity": "sha512-Rz0wKUa+LTW1PLXmwnf8mn85EBzaGSt6qamqtmnh9Hflkc+EqiYMhtUJeLdV+wsgYq4aG0ANc+bpUDpsUhdnwg==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-translations": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "copy-text-to-clipboard": "^3.0.1", - "infima": "0.2.0-alpha.42", + "infima": "0.2.0-alpha.43", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.14", @@ -2461,16 +2477,17 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.3.1.tgz", - "integrity": "sha512-RYmYl2OR2biO+yhmW1aS5FyEvnrItPINa+0U2dMxcHpah8reSCjQ9eJGRmAgkZFchV1+aIQzXOI1K7LCW38O0g==", - "dependencies": { - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/utils": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.4.1.tgz", + "integrity": "sha512-G7Zau1W5rQTaFFB3x3soQoZpkgMbl/SYNG8PfMFIjKa3M3q8n0m/GRf5/H/e5BqOvt8c+ZWIXGCiz+kUCSHovA==", + "dependencies": { + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2490,18 +2507,18 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.3.1.tgz", - "integrity": "sha512-JdHaRqRuH1X++g5fEMLnq7OtULSGQdrs9AbhcWRQ428ZB8/HOiaN6mj3hzHvcD3DFgu7koIVtWPQnvnN7iwzHA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.1.tgz", + "integrity": "sha512-6BcqW2lnLhZCXuMAvPRezFs1DpmEKzXFKlYjruuas+Xy3AQeFzDJKTJFIm49N77WFCTyxff8d3E4Q9pi/+5McQ==", "dependencies": { "@docsearch/react": "^3.1.1", - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-translations": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "algoliasearch": "^4.13.1", "algoliasearch-helper": "^3.10.0", "clsx": "^1.2.1", @@ -2520,9 +2537,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.3.1.tgz", - "integrity": "sha512-BsBZzAewJabVhoGG1Ij2u4pMS3MPW6gZ6sS4pc+Y7czevRpzxoFNJXRtQDVGe7mOpv/MmRmqg4owDK+lcOTCVQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.1.tgz", + "integrity": "sha512-T1RAGP+f86CA1kfE8ejZ3T3pUU3XcyvrGMfC/zxCtc2BsnoexuNI9Vk2CmuKCb+Tacvhxjv5unhxXce0+NKyvA==", "dependencies": { "fs-extra": "^10.1.0", "tslib": "^2.4.0" @@ -2532,9 +2549,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.3.1.tgz", - "integrity": "sha512-PREbIRhTaNNY042qmfSE372Jb7djZt+oVTZkoqHJ8eff8vOIc2zqqDqBVc5BhOfpZGPTrE078yy/torUEZy08A==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.1.tgz", + "integrity": "sha512-0R+cbhpMkhbRXX138UOc/2XZFF8hiZa6ooZAEEJFp5scytzCw4tC1gChMFXrpa3d2tYE6AX8IrOEpSonLmfQuQ==", "dependencies": { "@types/history": "^4.7.11", "@types/react": "*", @@ -2551,11 +2568,11 @@ } }, "node_modules/@docusaurus/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-9WcQROCV0MmrpOQDXDGhtGMd52DHpSFbKLfkyaYumzbTstrbA5pPOtiGtxK1nqUHkiIv8UwexS54p0Vod2I1lg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-1lvEZdAQhKNht9aPXPoh69eeKnV0/62ROhQeFKKxmzd0zkcuE/Oc5Gpnt00y/f5bIsmOsYMY7Pqfm/5rteT5GA==", "dependencies": { - "@docusaurus/logger": "2.3.1", + "@docusaurus/logger": "2.4.1", "@svgr/webpack": "^6.2.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -2585,9 +2602,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.3.1.tgz", - "integrity": "sha512-pVlRpXkdNcxmKNxAaB1ya2hfCEvVsLDp2joeM6K6uv55Oc5nVIqgyYSgSNKZyMdw66NnvMfsu0RBylcwZQKo9A==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.1.tgz", + "integrity": "sha512-bCVGdZU+z/qVcIiEQdyx0K13OC5mYwxhSuDUR95oFbKVuXYRrTVrwZIqQljuo1fyJvFTKHiL9L9skQOPokuFNQ==", "dependencies": { "tslib": "^2.4.0" }, @@ -2604,12 +2621,12 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.3.1.tgz", - "integrity": "sha512-7n0208IG3k1HVTByMHlZoIDjjOFC8sbViHVXJx0r3Q+3Ezrx+VQ1RZ/zjNn6lT+QBCRCXlnlaoJ8ug4HIVgQ3w==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.1.tgz", + "integrity": "sha512-unII3hlJlDwZ3w8U+pMO3Lx3RhI4YEbY3YNsQj4yzrkZzlpqZOLuAiZK2JyULnD+TKbceKU0WyWkQXtYbLNDFA==", "dependencies": { - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", "joi": "^17.6.0", "js-yaml": "^4.1.0", "tslib": "^2.4.0" @@ -3274,11 +3291,11 @@ } }, "node_modules/@types/hast": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", - "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.5.tgz", + "integrity": "sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg==", "dependencies": { - "@types/unist": "*" + "@types/unist": "^2" } }, "node_modules/@types/history": { @@ -3326,11 +3343,11 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "node_modules/@types/mdast": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.12.tgz", + "integrity": "sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==", "dependencies": { - "@types/unist": "*" + "@types/unist": "^2" } }, "node_modules/@types/mime": { @@ -3451,9 +3468,9 @@ } }, "node_modules/@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz", + "integrity": "sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==" }, "node_modules/@types/ws": { "version": "8.5.4", @@ -3789,30 +3806,30 @@ } }, "node_modules/algoliasearch": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.14.3.tgz", - "integrity": "sha512-GZTEuxzfWbP/vr7ZJfGzIl8fOsoxN916Z6FY2Egc9q2TmZ6hvq5KfAxY89pPW01oW/2HDEKA8d30f9iAH9eXYg==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.14.3", - "@algolia/cache-common": "4.14.3", - "@algolia/cache-in-memory": "4.14.3", - "@algolia/client-account": "4.14.3", - "@algolia/client-analytics": "4.14.3", - "@algolia/client-common": "4.14.3", - "@algolia/client-personalization": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/logger-common": "4.14.3", - "@algolia/logger-console": "4.14.3", - "@algolia/requester-browser-xhr": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/requester-node-http": "4.14.3", - "@algolia/transporter": "4.14.3" + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.18.0.tgz", + "integrity": "sha512-pCuVxC1SVcpc08ENH32T4sLKSyzoU7TkRIDBMwSLfIiW+fq4znOmWDkAygHZ6pRcO9I1UJdqlfgnV7TRj+MXrA==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.18.0", + "@algolia/cache-common": "4.18.0", + "@algolia/cache-in-memory": "4.18.0", + "@algolia/client-account": "4.18.0", + "@algolia/client-analytics": "4.18.0", + "@algolia/client-common": "4.18.0", + "@algolia/client-personalization": "4.18.0", + "@algolia/client-search": "4.18.0", + "@algolia/logger-common": "4.18.0", + "@algolia/logger-console": "4.18.0", + "@algolia/requester-browser-xhr": "4.18.0", + "@algolia/requester-common": "4.18.0", + "@algolia/requester-node-http": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.3.tgz", - "integrity": "sha512-TbaEvLwiuGygHQIB8y+OsJKQQ40+JKUua5B91X66tMUHyyhbNHvqyr0lqd3wCoyKx7WybyQrC0WJvzoIeh24Aw==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.13.3.tgz", + "integrity": "sha512-jhbbuYZ+fheXpaJlqdJdFa1jOsrTWKmRRTYDM3oVTto5VodZzM7tT+BHzslAotaJf/81CKrm6yLRQn8WIr/K4A==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -4794,9 +4811,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/copy-text-to-clipboard": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz", - "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", + "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", "engines": { "node": ">=12" }, @@ -4969,11 +4986,11 @@ } }, "node_modules/cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", "dependencies": { - "node-fetch": "2.6.7" + "node-fetch": "^2.6.12" } }, "node_modules/cross-spawn": { @@ -5569,13 +5586,13 @@ } }, "node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" + "domhandler": "^5.0.3" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" @@ -6036,9 +6053,9 @@ } }, "node_modules/fbjs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", - "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", "dependencies": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", @@ -6046,7 +6063,7 @@ "object-assign": "^4.1.0", "promise": "^7.1.1", "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.30" + "ua-parser-js": "^1.0.35" } }, "node_modules/fbjs-css-vars": { @@ -6179,9 +6196,9 @@ } }, "node_modules/flux": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", - "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", "dependencies": { "fbemitter": "^3.0.0", "fbjs": "^3.0.1" @@ -6909,9 +6926,9 @@ } }, "node_modules/htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", - "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -6921,9 +6938,9 @@ ], "dependencies": { "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "domutils": "^3.0.1", - "entities": "^4.3.0" + "entities": "^4.4.0" } }, "node_modules/http-cache-semantics": { @@ -7104,9 +7121,9 @@ } }, "node_modules/infima": { - "version": "0.2.0-alpha.42", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.42.tgz", - "integrity": "sha512-ift8OXNbQQwtbIt6z16KnSWP7uJ/SysSMFI4F87MNRTicypfl4Pv3E2OGVv6N3nSZFJvA8imYulCBS64iyHYww==", + "version": "0.2.0-alpha.43", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", + "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", "engines": { "node": ">=12" } @@ -8109,9 +8126,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -9223,9 +9240,9 @@ } }, "node_modules/postcss-sort-media-queries": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.3.0.tgz", - "integrity": "sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", + "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", "dependencies": { "sort-css-media-queries": "2.1.0" }, @@ -9794,11 +9811,11 @@ } }, "node_modules/react-textarea-autosize": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz", - "integrity": "sha512-YrTFaEHLgJsi8sJVYHBzYn+mkP3prGkmP2DKb/tm0t7CLJY5t1Rxix8070LAKb0wby7bl/lf2EeHkuMihMZMwQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.2.tgz", + "integrity": "sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==", "dependencies": { - "@babel/runtime": "^7.10.2", + "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, @@ -10482,6 +10499,15 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/search-insights": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.7.0.tgz", + "integrity": "sha512-GLbVaGgzYEKMvuJbHRhLi1qoBFnjXZGZ6l4LxOYPCp4lI2jDRB3jPU9/XNhMwv6kvnA9slTreq6pvK+b3o3aqg==", + "peer": true, + "engines": { + "node": ">=8.16.0" + } + }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -11545,9 +11571,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz", + "integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==", "funding": [ { "type": "opencollective", @@ -12749,95 +12775,105 @@ }, "dependencies": { "@algolia/autocomplete-core": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz", - "integrity": "sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", + "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", "requires": { - "@algolia/autocomplete-shared": "1.7.4" + "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", + "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "requires": { + "@algolia/autocomplete-shared": "1.9.3" } }, "@algolia/autocomplete-preset-algolia": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.4.tgz", - "integrity": "sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", + "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", "requires": { - "@algolia/autocomplete-shared": "1.7.4" + "@algolia/autocomplete-shared": "1.9.3" } }, "@algolia/autocomplete-shared": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.4.tgz", - "integrity": "sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg==" + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", + "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "requires": {} }, "@algolia/cache-browser-local-storage": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.3.tgz", - "integrity": "sha512-hWH1yCxgG3+R/xZIscmUrWAIBnmBFHH5j30fY/+aPkEZWt90wYILfAHIOZ1/Wxhho5SkPfwFmT7ooX2d9JeQBw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.18.0.tgz", + "integrity": "sha512-rUAs49NLlO8LVLgGzM4cLkw8NJLKguQLgvFmBEe3DyzlinoqxzQMHfKZs6TSq4LZfw/z8qHvRo8NcTAAUJQLcw==", "requires": { - "@algolia/cache-common": "4.14.3" + "@algolia/cache-common": "4.18.0" } }, "@algolia/cache-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.14.3.tgz", - "integrity": "sha512-oZJofOoD9FQOwiGTzyRnmzvh3ZP8WVTNPBLH5xU5JNF7drDbRT0ocVT0h/xB2rPHYzOeXRrLaQQBwRT/CKom0Q==" + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.18.0.tgz", + "integrity": "sha512-BmxsicMR4doGbeEXQu8yqiGmiyvpNvejYJtQ7rvzttEAMxOPoWEHrWyzBQw4x7LrBY9pMrgv4ZlUaF8PGzewHg==" }, "@algolia/cache-in-memory": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.14.3.tgz", - "integrity": "sha512-ES0hHQnzWjeioLQf5Nq+x1AWdZJ50znNPSH3puB/Y4Xsg4Av1bvLmTJe7SY2uqONaeMTvL0OaVcoVtQgJVw0vg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.18.0.tgz", + "integrity": "sha512-evD4dA1nd5HbFdufBxLqlJoob7E2ozlqJZuV3YlirNx5Na4q1LckIuzjNYZs2ddLzuTc/Xd5O3Ibf7OwPskHxw==", "requires": { - "@algolia/cache-common": "4.14.3" + "@algolia/cache-common": "4.18.0" } }, "@algolia/client-account": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.14.3.tgz", - "integrity": "sha512-PBcPb0+f5Xbh5UfLZNx2Ow589OdP8WYjB4CnvupfYBrl9JyC1sdH4jcq/ri8osO/mCZYjZrQsKAPIqW/gQmizQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.18.0.tgz", + "integrity": "sha512-XsDnlROr3+Z1yjxBJjUMfMazi1V155kVdte6496atvBgOEtwCzTs3A+qdhfsAnGUvaYfBrBkL0ThnhMIBCGcew==", "requires": { - "@algolia/client-common": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.18.0", + "@algolia/client-search": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "@algolia/client-analytics": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.14.3.tgz", - "integrity": "sha512-eAwQq0Hb/aauv9NhCH5Dp3Nm29oFx28sayFN2fdOWemwSeJHIl7TmcsxVlRsO50fsD8CtPcDhtGeD3AIFLNvqw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.18.0.tgz", + "integrity": "sha512-chEUSN4ReqU7uRQ1C8kDm0EiPE+eJeAXiWcBwLhEynfNuTfawN9P93rSZktj7gmExz0C8XmkbBU19IQ05wCNrQ==", "requires": { - "@algolia/client-common": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.18.0", + "@algolia/client-search": "4.18.0", + "@algolia/requester-common": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "@algolia/client-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.14.3.tgz", - "integrity": "sha512-jkPPDZdi63IK64Yg4WccdCsAP4pHxSkr4usplkUZM5C1l1oEpZXsy2c579LQ0rvwCs5JFmwfNG4ahOszidfWPw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.18.0.tgz", + "integrity": "sha512-7N+soJFP4wn8tjTr3MSUT/U+4xVXbz4jmeRfWfVAzdAbxLAQbHa0o/POSdTvQ8/02DjCLelloZ1bb4ZFVKg7Wg==", "requires": { - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/requester-common": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "@algolia/client-personalization": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.14.3.tgz", - "integrity": "sha512-UCX1MtkVNgaOL9f0e22x6tC9e2H3unZQlSUdnVaSKpZ+hdSChXGaRjp2UIT7pxmPqNCyv51F597KEX5WT60jNg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.18.0.tgz", + "integrity": "sha512-+PeCjODbxtamHcPl+couXMeHEefpUpr7IHftj4Y4Nia1hj8gGq4VlIcqhToAw8YjLeCTfOR7r7xtj3pJcYdP8A==", "requires": { - "@algolia/client-common": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.18.0", + "@algolia/requester-common": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "@algolia/client-search": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.14.3.tgz", - "integrity": "sha512-I2U7xBx5OPFdPLA8AXKUPPxGY3HDxZ4r7+mlZ8ZpLbI8/ri6fnu6B4z3wcL7sgHhDYMwnAE8Xr0AB0h3Hnkp4A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.18.0.tgz", + "integrity": "sha512-F9xzQXTjm6UuZtnsLIew6KSraXQ0AzS/Ee+OD+mQbtcA/K1sg89tqb8TkwjtiYZ0oij13u3EapB3gPZwm+1Y6g==", "requires": { - "@algolia/client-common": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.18.0", + "@algolia/requester-common": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "@algolia/events": { @@ -12846,47 +12882,47 @@ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, "@algolia/logger-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.14.3.tgz", - "integrity": "sha512-kUEAZaBt/J3RjYi8MEBT2QEexJR2kAE2mtLmezsmqMQZTV502TkHCxYzTwY2dE7OKcUTxi4OFlMuS4GId9CWPw==" + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.18.0.tgz", + "integrity": "sha512-46etYgSlkoKepkMSyaoriSn2JDgcrpc/nkOgou/lm0y17GuMl9oYZxwKKTSviLKI5Irk9nSKGwnBTQYwXOYdRg==" }, "@algolia/logger-console": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.14.3.tgz", - "integrity": "sha512-ZWqAlUITktiMN2EiFpQIFCJS10N96A++yrexqC2Z+3hgF/JcKrOxOdT4nSCQoEPvU4Ki9QKbpzbebRDemZt/hw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.18.0.tgz", + "integrity": "sha512-3P3VUYMl9CyJbi/UU1uUNlf6Z8N2ltW3Oqhq/nR7vH0CjWv32YROq3iGWGxB2xt3aXobdUPXs6P0tHSKRmNA6g==", "requires": { - "@algolia/logger-common": "4.14.3" + "@algolia/logger-common": "4.18.0" } }, "@algolia/requester-browser-xhr": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.3.tgz", - "integrity": "sha512-AZeg2T08WLUPvDncl2XLX2O67W5wIO8MNaT7z5ii5LgBTuk/rU4CikTjCe2xsUleIZeFl++QrPAi4Bdxws6r/Q==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.18.0.tgz", + "integrity": "sha512-/AcWHOBub2U4TE/bPi4Gz1XfuLK6/7dj4HJG+Z2SfQoS1RjNLshZclU3OoKIkFp8D2NC7+BNsPvr9cPLyW8nyQ==", "requires": { - "@algolia/requester-common": "4.14.3" + "@algolia/requester-common": "4.18.0" } }, "@algolia/requester-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.14.3.tgz", - "integrity": "sha512-RrRzqNyKFDP7IkTuV3XvYGF9cDPn9h6qEDl595lXva3YUk9YSS8+MGZnnkOMHvjkrSCKfoLeLbm/T4tmoIeclw==" + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.18.0.tgz", + "integrity": "sha512-xlT8R1qYNRBCi1IYLsx7uhftzdfsLPDGudeQs+xvYB4sQ3ya7+ppolB/8m/a4F2gCkEO6oxpp5AGemM7kD27jA==" }, "@algolia/requester-node-http": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.14.3.tgz", - "integrity": "sha512-O5wnPxtDRPuW2U0EaOz9rMMWdlhwP0J0eSL1Z7TtXF8xnUeeUyNJrdhV5uy2CAp6RbhM1VuC3sOJcIR6Av+vbA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.18.0.tgz", + "integrity": "sha512-TGfwj9aeTVgOUhn5XrqBhwUhUUDnGIKlI0kCBMdR58XfXcfdwomka+CPIgThRbfYw04oQr31A6/95ZH2QVJ9UQ==", "requires": { - "@algolia/requester-common": "4.14.3" + "@algolia/requester-common": "4.18.0" } }, "@algolia/transporter": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.14.3.tgz", - "integrity": "sha512-2qlKlKsnGJ008exFRb5RTeTOqhLZj0bkMCMVskxoqWejs2Q2QtWmsiH98hDfpw0fmnyhzHEt0Z7lqxBYp8bW2w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.18.0.tgz", + "integrity": "sha512-xbw3YRUGtXQNG1geYFEDDuFLZt4Z8YNKbamHPkzr3rWc6qp4/BqEeXcI2u/P/oMq2yxtXgMxrCxOPA8lyIe5jw==", "requires": { - "@algolia/cache-common": "4.14.3", - "@algolia/logger-common": "4.14.3", - "@algolia/requester-common": "4.14.3" + "@algolia/cache-common": "4.18.0", + "@algolia/logger-common": "4.18.0", + "@algolia/requester-common": "4.18.0" } }, "@ampproject/remapping": { @@ -14118,25 +14154,25 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" }, "@docsearch/css": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.3.3.tgz", - "integrity": "sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg==" + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.1.tgz", + "integrity": "sha512-2Pu9HDg/uP/IT10rbQ+4OrTQuxIWdKVUEdcw9/w7kZJv9NeHS6skJx1xuRiFyoGKwAzcHXnLp7csE99sj+O1YA==" }, "@docsearch/react": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.3.3.tgz", - "integrity": "sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.1.tgz", + "integrity": "sha512-t5mEODdLzZq4PTFAm/dvqcvZFdPDMdfPE5rJS5SC8OUq9mPzxEy6b+9THIqNM9P0ocCb4UC5jqBrxKclnuIbzQ==", "requires": { - "@algolia/autocomplete-core": "1.7.4", - "@algolia/autocomplete-preset-algolia": "1.7.4", - "@docsearch/css": "3.3.3", + "@algolia/autocomplete-core": "1.9.3", + "@algolia/autocomplete-preset-algolia": "1.9.3", + "@docsearch/css": "3.5.1", "algoliasearch": "^4.0.0" } }, "@docusaurus/core": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.3.1.tgz", - "integrity": "sha512-0Jd4jtizqnRAr7svWaBbbrCCN8mzBNd2xFLoT/IM7bGfFie5y58oz97KzXliwiLY3zWjqMXjQcuP1a5VgCv2JA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.1.tgz", + "integrity": "sha512-SNsY7PshK3Ri7vtsLXVeAJGS50nJN3RgF836zkyUfAD01Fq+sAk5EwWgLw+nnm5KVNGDu7PRR2kRGDsWvqpo0g==", "requires": { "@babel/core": "^7.18.6", "@babel/generator": "^7.18.7", @@ -14148,13 +14184,13 @@ "@babel/runtime": "^7.18.6", "@babel/runtime-corejs3": "^7.18.6", "@babel/traverse": "^7.18.8", - "@docusaurus/cssnano-preset": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", + "@docusaurus/cssnano-preset": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "@slorber/static-site-generator-webpack-plugin": "^4.0.7", "@svgr/webpack": "^6.2.1", "autoprefixer": "^10.4.7", @@ -14212,9 +14248,9 @@ } }, "@docusaurus/cssnano-preset": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.3.1.tgz", - "integrity": "sha512-7mIhAROES6CY1GmCjR4CZkUfjTL6B3u6rKHK0ChQl2d1IevYXq/k/vFgvOrJfcKxiObpMnE9+X6R2Wt1KqxC6w==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.1.tgz", + "integrity": "sha512-ka+vqXwtcW1NbXxWsh6yA1Ckii1klY9E53cJ4O9J09nkMBgrNX3iEFED1fWdv8wf4mJjvGi5RLZ2p9hJNjsLyQ==", "requires": { "cssnano-preset-advanced": "^5.3.8", "postcss": "^8.4.14", @@ -14223,23 +14259,23 @@ } }, "@docusaurus/logger": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.3.1.tgz", - "integrity": "sha512-2lAV/olKKVr9qJhfHFCaqBIl8FgYjbUFwgUnX76+cULwQYss+42ZQ3grHGFvI0ocN2X55WcYe64ellQXz7suqg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.4.1.tgz", + "integrity": "sha512-5h5ysIIWYIDHyTVd8BjheZmQZmEgWDR54aQ1BX9pjFfpyzFo5puKXKYrYJXbjEHGyVhEzmB9UXwbxGfaZhOjcg==", "requires": { "chalk": "^4.1.2", "tslib": "^2.4.0" } }, "@docusaurus/mdx-loader": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.3.1.tgz", - "integrity": "sha512-Gzga7OsxQRpt3392K9lv/bW4jGppdLFJh3luKRknCKSAaZrmVkOQv2gvCn8LAOSZ3uRg5No7AgYs/vpL8K94lA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.1.tgz", + "integrity": "sha512-4KhUhEavteIAmbBj7LVFnrVYDiU51H5YWW1zY6SmBSte/YLhDutztLTBE0PQl1Grux1jzUJeaSvAzHpTn6JJDQ==", "requires": { "@babel/parser": "^7.18.8", "@babel/traverse": "^7.18.8", - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", "@mdx-js/mdx": "^1.6.22", "escape-html": "^1.0.3", "file-loader": "^6.2.0", @@ -14256,12 +14292,12 @@ } }, "@docusaurus/module-type-aliases": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.3.1.tgz", - "integrity": "sha512-6KkxfAVOJqIUynTRb/tphYCl+co3cP0PlHiMDbi+SzmYxMdgIrwYqH9yAnGSDoN6Jk2ZE/JY/Azs/8LPgKP48A==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.1.tgz", + "integrity": "sha512-gLBuIFM8Dp2XOCWffUDSjtxY7jQgKvYujt7Mx5s4FCTfoL5dN1EVbnrn+O2Wvh8b0a77D57qoIDY7ghgmatR1A==", "requires": { "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "2.3.1", + "@docusaurus/types": "2.4.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -14271,15 +14307,15 @@ } }, "@docusaurus/plugin-client-redirects": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.3.1.tgz", - "integrity": "sha512-Ye0z36/L8685ni0DIxHqPPaHIXFXiSF90QYiYfpODBX6NxvvveUTyylsDBU1GQhPXPn1bd39QgaOuZ+j9gfaog==", - "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.4.1.tgz", + "integrity": "sha512-tp0j16gaLIJ4p+IR0P6KDOFsTOGGMY54MNPnmM61Vaqqt5omLqsuKUO8UlCGU1oW/4EIQOhXYy99XYY5MjE+7A==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "eta": "^2.0.0", "fs-extra": "^10.1.0", "lodash": "^4.17.21", @@ -14287,17 +14323,17 @@ } }, "@docusaurus/plugin-content-blog": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.3.1.tgz", - "integrity": "sha512-f5LjqX+9WkiLyGiQ41x/KGSJ/9bOjSD8lsVhPvYeUYHCtYpuiDKfhZE07O4EqpHkBx4NQdtQDbp+aptgHSTuiw==", - "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.1.tgz", + "integrity": "sha512-E2i7Knz5YIbE1XELI6RlTnZnGgS52cUO4BlCiCUCvQHbR+s1xeIWz4C6BtaVnlug0Ccz7nFSksfwDpVlkujg5Q==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^10.1.0", @@ -14310,17 +14346,17 @@ } }, "@docusaurus/plugin-content-docs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.3.1.tgz", - "integrity": "sha512-DxztTOBEruv7qFxqUtbsqXeNcHqcVEIEe+NQoI1oi2DBmKBhW/o0MIal8lt+9gvmpx3oYtlwmLOOGepxZgJGkw==", - "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.1.tgz", + "integrity": "sha512-Lo7lSIcpswa2Kv4HEeUcGYqaasMUQNpjTXpV0N8G6jXgZaQurqp7E8NGYeGbDXnb48czmHWbzDL4S3+BbK0VzA==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "@types/react-router-config": "^5.0.6", "combine-promises": "^1.1.0", "fs-extra": "^10.1.0", @@ -14333,100 +14369,100 @@ } }, "@docusaurus/plugin-content-pages": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.3.1.tgz", - "integrity": "sha512-E80UL6hvKm5VVw8Ka8YaVDtO6kWWDVUK4fffGvkpQ/AJQDOg99LwOXKujPoICC22nUFTsZ2Hp70XvpezCsFQaA==", - "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.1.tgz", + "integrity": "sha512-/UjuH/76KLaUlL+o1OvyORynv6FURzjurSjvn2lbWTFc4tpYY2qLYTlKpTCBVPhlLUQsfyFnshEJDLmPneq2oA==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "fs-extra": "^10.1.0", "tslib": "^2.4.0", "webpack": "^5.73.0" } }, "@docusaurus/plugin-debug": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.3.1.tgz", - "integrity": "sha512-Ujpml1Ppg4geB/2hyu2diWnO49az9U2bxM9Shen7b6qVcyFisNJTkVG2ocvLC7wM1efTJcUhBO6zAku2vKJGMw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.4.1.tgz", + "integrity": "sha512-7Yu9UPzRShlrH/G8btOpR0e6INFZr0EegWplMjOqelIwAcx3PKyR8mgPTxGTxcqiYj6hxSCRN0D8R7YrzImwNA==", "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", "fs-extra": "^10.1.0", "react-json-view": "^1.21.3", "tslib": "^2.4.0" } }, "@docusaurus/plugin-google-analytics": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.3.1.tgz", - "integrity": "sha512-OHip0GQxKOFU8n7gkt3TM4HOYTXPCFDjqKbMClDD3KaDnyTuMp/Zvd9HSr770lLEscgPWIvzhJByRAClqsUWiQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.1.tgz", + "integrity": "sha512-dyZJdJiCoL+rcfnm0RPkLt/o732HvLiEwmtoNzOoz9MSZz117UH2J6U2vUDtzUzwtFLIf32KkeyzisbwUCgcaQ==", "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "tslib": "^2.4.0" } }, "@docusaurus/plugin-google-gtag": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.3.1.tgz", - "integrity": "sha512-uXtDhfu4+Hm+oqWUySr3DNI5cWC/rmP6XJyAk83Heor3dFjZqDwCbkX8yWPywkRiWev3Dk/rVF8lEn0vIGVocA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.1.tgz", + "integrity": "sha512-mKIefK+2kGTQBYvloNEKtDmnRD7bxHLsBcxgnbt4oZwzi2nxCGjPX6+9SQO2KCN5HZbNrYmGo5GJfMgoRvy6uA==", "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "tslib": "^2.4.0" } }, "@docusaurus/plugin-google-tag-manager": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.3.1.tgz", - "integrity": "sha512-Ww2BPEYSqg8q8tJdLYPFFM3FMDBCVhEM4UUqKzJaiRMx3NEoly3qqDRAoRDGdIhlC//Rf0iJV9cWAoq2m6k3sw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.1.tgz", + "integrity": "sha512-Zg4Ii9CMOLfpeV2nG74lVTWNtisFaH9QNtEw48R5QE1KIwDBdTVaiSA18G1EujZjrzJJzXN79VhINSbOJO/r3g==", "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "tslib": "^2.4.0" } }, "@docusaurus/plugin-sitemap": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.3.1.tgz", - "integrity": "sha512-8Yxile/v6QGYV9vgFiYL+8d2N4z4Er3pSHsrD08c5XI8bUXxTppMwjarDUTH/TRTfgAWotRbhJ6WZLyajLpozA==", - "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.1.tgz", + "integrity": "sha512-lZx+ijt/+atQ3FVE8FOHV/+X3kuok688OydDXrqKRJyXBJZKgGjA2Qa8RjQ4f27V2woaXhtnyrdPop/+OjVMRg==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "fs-extra": "^10.1.0", "sitemap": "^7.1.1", "tslib": "^2.4.0" } }, "@docusaurus/preset-classic": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.3.1.tgz", - "integrity": "sha512-OQ5W0AHyfdUk0IldwJ3BlnZ1EqoJuu2L2BMhqLbqwNWdkmzmSUvlFLH1Pe7CZSQgB2YUUC/DnmjbPKk/qQD0lQ==", - "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/plugin-debug": "2.3.1", - "@docusaurus/plugin-google-analytics": "2.3.1", - "@docusaurus/plugin-google-gtag": "2.3.1", - "@docusaurus/plugin-google-tag-manager": "2.3.1", - "@docusaurus/plugin-sitemap": "2.3.1", - "@docusaurus/theme-classic": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-search-algolia": "2.3.1", - "@docusaurus/types": "2.3.1" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.4.1.tgz", + "integrity": "sha512-P4//+I4zDqQJ+UDgoFrjIFaQ1MeS9UD1cvxVQaI6O7iBmiHQm0MGROP1TbE7HlxlDPXFJjZUK3x3cAoK63smGQ==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/plugin-debug": "2.4.1", + "@docusaurus/plugin-google-analytics": "2.4.1", + "@docusaurus/plugin-google-gtag": "2.4.1", + "@docusaurus/plugin-google-tag-manager": "2.4.1", + "@docusaurus/plugin-sitemap": "2.4.1", + "@docusaurus/theme-classic": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-search-algolia": "2.4.1", + "@docusaurus/types": "2.4.1" } }, "@docusaurus/react-loadable": { @@ -14439,26 +14475,26 @@ } }, "@docusaurus/theme-classic": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.3.1.tgz", - "integrity": "sha512-SelSIDvyttb7ZYHj8vEUhqykhAqfOPKk+uP0z85jH72IMC58e7O8DIlcAeBv+CWsLbNIl9/Hcg71X0jazuxJug==", - "requires": { - "@docusaurus/core": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.4.1.tgz", + "integrity": "sha512-Rz0wKUa+LTW1PLXmwnf8mn85EBzaGSt6qamqtmnh9Hflkc+EqiYMhtUJeLdV+wsgYq4aG0ANc+bpUDpsUhdnwg==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-translations": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "copy-text-to-clipboard": "^3.0.1", - "infima": "0.2.0-alpha.42", + "infima": "0.2.0-alpha.43", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.14", @@ -14471,16 +14507,17 @@ } }, "@docusaurus/theme-common": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.3.1.tgz", - "integrity": "sha512-RYmYl2OR2biO+yhmW1aS5FyEvnrItPINa+0U2dMxcHpah8reSCjQ9eJGRmAgkZFchV1+aIQzXOI1K7LCW38O0g==", - "requires": { - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/utils": "2.3.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.4.1.tgz", + "integrity": "sha512-G7Zau1W5rQTaFFB3x3soQoZpkgMbl/SYNG8PfMFIjKa3M3q8n0m/GRf5/H/e5BqOvt8c+ZWIXGCiz+kUCSHovA==", + "requires": { + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -14493,18 +14530,18 @@ } }, "@docusaurus/theme-search-algolia": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.3.1.tgz", - "integrity": "sha512-JdHaRqRuH1X++g5fEMLnq7OtULSGQdrs9AbhcWRQ428ZB8/HOiaN6mj3hzHvcD3DFgu7koIVtWPQnvnN7iwzHA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.1.tgz", + "integrity": "sha512-6BcqW2lnLhZCXuMAvPRezFs1DpmEKzXFKlYjruuas+Xy3AQeFzDJKTJFIm49N77WFCTyxff8d3E4Q9pi/+5McQ==", "requires": { "@docsearch/react": "^3.1.1", - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-translations": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", "algoliasearch": "^4.13.1", "algoliasearch-helper": "^3.10.0", "clsx": "^1.2.1", @@ -14516,18 +14553,18 @@ } }, "@docusaurus/theme-translations": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.3.1.tgz", - "integrity": "sha512-BsBZzAewJabVhoGG1Ij2u4pMS3MPW6gZ6sS4pc+Y7czevRpzxoFNJXRtQDVGe7mOpv/MmRmqg4owDK+lcOTCVQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.1.tgz", + "integrity": "sha512-T1RAGP+f86CA1kfE8ejZ3T3pUU3XcyvrGMfC/zxCtc2BsnoexuNI9Vk2CmuKCb+Tacvhxjv5unhxXce0+NKyvA==", "requires": { "fs-extra": "^10.1.0", "tslib": "^2.4.0" } }, "@docusaurus/types": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.3.1.tgz", - "integrity": "sha512-PREbIRhTaNNY042qmfSE372Jb7djZt+oVTZkoqHJ8eff8vOIc2zqqDqBVc5BhOfpZGPTrE078yy/torUEZy08A==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.1.tgz", + "integrity": "sha512-0R+cbhpMkhbRXX138UOc/2XZFF8hiZa6ooZAEEJFp5scytzCw4tC1gChMFXrpa3d2tYE6AX8IrOEpSonLmfQuQ==", "requires": { "@types/history": "^4.7.11", "@types/react": "*", @@ -14540,11 +14577,11 @@ } }, "@docusaurus/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-9WcQROCV0MmrpOQDXDGhtGMd52DHpSFbKLfkyaYumzbTstrbA5pPOtiGtxK1nqUHkiIv8UwexS54p0Vod2I1lg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-1lvEZdAQhKNht9aPXPoh69eeKnV0/62ROhQeFKKxmzd0zkcuE/Oc5Gpnt00y/f5bIsmOsYMY7Pqfm/5rteT5GA==", "requires": { - "@docusaurus/logger": "2.3.1", + "@docusaurus/logger": "2.4.1", "@svgr/webpack": "^6.2.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -14563,20 +14600,20 @@ } }, "@docusaurus/utils-common": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.3.1.tgz", - "integrity": "sha512-pVlRpXkdNcxmKNxAaB1ya2hfCEvVsLDp2joeM6K6uv55Oc5nVIqgyYSgSNKZyMdw66NnvMfsu0RBylcwZQKo9A==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.1.tgz", + "integrity": "sha512-bCVGdZU+z/qVcIiEQdyx0K13OC5mYwxhSuDUR95oFbKVuXYRrTVrwZIqQljuo1fyJvFTKHiL9L9skQOPokuFNQ==", "requires": { "tslib": "^2.4.0" } }, "@docusaurus/utils-validation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.3.1.tgz", - "integrity": "sha512-7n0208IG3k1HVTByMHlZoIDjjOFC8sbViHVXJx0r3Q+3Ezrx+VQ1RZ/zjNn6lT+QBCRCXlnlaoJ8ug4HIVgQ3w==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.1.tgz", + "integrity": "sha512-unII3hlJlDwZ3w8U+pMO3Lx3RhI4YEbY3YNsQj4yzrkZzlpqZOLuAiZK2JyULnD+TKbceKU0WyWkQXtYbLNDFA==", "requires": { - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", "joi": "^17.6.0", "js-yaml": "^4.1.0", "tslib": "^2.4.0" @@ -15046,11 +15083,11 @@ } }, "@types/hast": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", - "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.5.tgz", + "integrity": "sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg==", "requires": { - "@types/unist": "*" + "@types/unist": "^2" } }, "@types/history": { @@ -15098,11 +15135,11 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "@types/mdast": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.12.tgz", + "integrity": "sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==", "requires": { - "@types/unist": "*" + "@types/unist": "^2" } }, "@types/mime": { @@ -15223,9 +15260,9 @@ } }, "@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz", + "integrity": "sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==" }, "@types/ws": { "version": "8.5.4", @@ -15514,30 +15551,30 @@ "requires": {} }, "algoliasearch": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.14.3.tgz", - "integrity": "sha512-GZTEuxzfWbP/vr7ZJfGzIl8fOsoxN916Z6FY2Egc9q2TmZ6hvq5KfAxY89pPW01oW/2HDEKA8d30f9iAH9eXYg==", - "requires": { - "@algolia/cache-browser-local-storage": "4.14.3", - "@algolia/cache-common": "4.14.3", - "@algolia/cache-in-memory": "4.14.3", - "@algolia/client-account": "4.14.3", - "@algolia/client-analytics": "4.14.3", - "@algolia/client-common": "4.14.3", - "@algolia/client-personalization": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/logger-common": "4.14.3", - "@algolia/logger-console": "4.14.3", - "@algolia/requester-browser-xhr": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/requester-node-http": "4.14.3", - "@algolia/transporter": "4.14.3" + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.18.0.tgz", + "integrity": "sha512-pCuVxC1SVcpc08ENH32T4sLKSyzoU7TkRIDBMwSLfIiW+fq4znOmWDkAygHZ6pRcO9I1UJdqlfgnV7TRj+MXrA==", + "requires": { + "@algolia/cache-browser-local-storage": "4.18.0", + "@algolia/cache-common": "4.18.0", + "@algolia/cache-in-memory": "4.18.0", + "@algolia/client-account": "4.18.0", + "@algolia/client-analytics": "4.18.0", + "@algolia/client-common": "4.18.0", + "@algolia/client-personalization": "4.18.0", + "@algolia/client-search": "4.18.0", + "@algolia/logger-common": "4.18.0", + "@algolia/logger-console": "4.18.0", + "@algolia/requester-browser-xhr": "4.18.0", + "@algolia/requester-common": "4.18.0", + "@algolia/requester-node-http": "4.18.0", + "@algolia/transporter": "4.18.0" } }, "algoliasearch-helper": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.3.tgz", - "integrity": "sha512-TbaEvLwiuGygHQIB8y+OsJKQQ40+JKUua5B91X66tMUHyyhbNHvqyr0lqd3wCoyKx7WybyQrC0WJvzoIeh24Aw==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.13.3.tgz", + "integrity": "sha512-jhbbuYZ+fheXpaJlqdJdFa1jOsrTWKmRRTYDM3oVTto5VodZzM7tT+BHzslAotaJf/81CKrm6yLRQn8WIr/K4A==", "requires": { "@algolia/events": "^4.0.1" } @@ -16249,9 +16286,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "copy-text-to-clipboard": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz", - "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", + "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==" }, "copy-webpack-plugin": { "version": "11.0.0", @@ -16364,11 +16401,11 @@ } }, "cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", "requires": { - "node-fetch": "2.6.7" + "node-fetch": "^2.6.12" } }, "cross-spawn": { @@ -16768,13 +16805,13 @@ } }, "domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "requires": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" + "domhandler": "^5.0.3" } }, "dot-case": { @@ -17136,9 +17173,9 @@ } }, "fbjs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", - "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", "requires": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", @@ -17146,7 +17183,7 @@ "object-assign": "^4.1.0", "promise": "^7.1.1", "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.30" + "ua-parser-js": "^1.0.35" } }, "fbjs-css-vars": { @@ -17245,9 +17282,9 @@ } }, "flux": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", - "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", "requires": { "fbemitter": "^3.0.0", "fbjs": "^3.0.1" @@ -17781,14 +17818,14 @@ } }, "htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", - "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "requires": { "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "domutils": "^3.0.1", - "entities": "^4.3.0" + "entities": "^4.4.0" } }, "http-cache-semantics": { @@ -17909,9 +17946,9 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" }, "infima": { - "version": "0.2.0-alpha.42", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.42.tgz", - "integrity": "sha512-ift8OXNbQQwtbIt6z16KnSWP7uJ/SysSMFI4F87MNRTicypfl4Pv3E2OGVv6N3nSZFJvA8imYulCBS64iyHYww==" + "version": "0.2.0-alpha.43", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", + "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==" }, "inflight": { "version": "1.0.6", @@ -18625,9 +18662,9 @@ } }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "requires": { "whatwg-url": "^5.0.0" } @@ -19326,9 +19363,9 @@ } }, "postcss-sort-media-queries": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.3.0.tgz", - "integrity": "sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", + "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", "requires": { "sort-css-media-queries": "2.1.0" } @@ -19749,11 +19786,11 @@ } }, "react-textarea-autosize": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz", - "integrity": "sha512-YrTFaEHLgJsi8sJVYHBzYn+mkP3prGkmP2DKb/tm0t7CLJY5t1Rxix8070LAKb0wby7bl/lf2EeHkuMihMZMwQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.2.tgz", + "integrity": "sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==", "requires": { - "@babel/runtime": "^7.10.2", + "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" } @@ -20249,6 +20286,12 @@ "ajv-keywords": "^3.5.2" } }, + "search-insights": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.7.0.tgz", + "integrity": "sha512-GLbVaGgzYEKMvuJbHRhLi1qoBFnjXZGZ6l4LxOYPCp4lI2jDRB3jPU9/XNhMwv6kvnA9slTreq6pvK+b3o3aqg==", + "peer": true + }, "section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -21049,9 +21092,9 @@ "peer": true }, "ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==" + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz", + "integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==" }, "unherit": { "version": "1.1.3", diff --git a/docs/package.json b/docs/package.json index 297cedf242..a2b516536d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,10 +14,10 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/plugin-client-redirects": "^2.3.1", - "@docusaurus/plugin-google-analytics": "^2.3.1", - "@docusaurus/preset-classic": "2.3.1", + "@docusaurus/core": "^2.4.1", + "@docusaurus/plugin-client-redirects": "^2.4.1", + "@docusaurus/plugin-google-analytics": "^2.4.1", + "@docusaurus/preset-classic": "^2.4.1", "@mdx-js/react": "^1.6.22", "@you54f/theme-github-codeblock": "^0.1.1", "autoprefixer": "^10.4.13", @@ -30,7 +30,7 @@ "tailwindcss": "^3.2.7" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1" + "@docusaurus/module-type-aliases": "^2.4.1" }, "browserslist": { "production": [ From 6204bd8ee6a3eb55fd5cf522d71cfb95a1756084 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Wed, 19 Jul 2023 08:05:46 -0700 Subject: [PATCH 24/38] chore: make hardcoded json string for consumer genesis readable (#1161) make readable Co-authored-by: Marius Poke --- x/ccv/provider/keeper/proposal_test.go | 85 +++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 4e9ee3f4ea..c84369e815 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -852,10 +852,91 @@ func TestMakeConsumerGenesis(t *testing.T) { actualGenesis, _, err := providerKeeper.MakeConsumerGenesis(ctx, &prop) require.NoError(t, err) - jsonString := `{"params":{"enabled":true, "blocks_per_distribution_transmission":1000, "ccv_timeout_period":2419200000000000, "transfer_timeout_period": 3600000000000, "consumer_redistribution_fraction":"0.75", "historical_entries":10000, "unbonding_period": 1728000000000000, "soft_opt_out_threshold": "0.05", "reward_denoms": [], "provider_reward_denoms": []},"new_chain":true,"provider_client_state":{"chain_id":"testchain1","trust_level":{"numerator":1,"denominator":3},"trusting_period":1197504000000000,"unbonding_period":1814400000000000,"max_clock_drift":10000000000,"frozen_height":{},"latest_height":{"revision_height":5},"proof_specs":[{"leaf_spec":{"hash":1,"prehash_value":1,"length":1,"prefix":"AA=="},"inner_spec":{"child_order":[0,1],"child_size":33,"min_prefix_length":4,"max_prefix_length":12,"hash":1}},{"leaf_spec":{"hash":1,"prehash_value":1,"length":1,"prefix":"AA=="},"inner_spec":{"child_order":[0,1],"child_size":32,"min_prefix_length":1,"max_prefix_length":1,"hash":1}}],"upgrade_path":["upgrade","upgradedIBCState"],"allow_update_after_expiry":true,"allow_update_after_misbehaviour":true},"provider_consensus_state":{"timestamp":"2020-01-02T00:00:10Z","root":{"hash":"LpGpeyQVLUo9HpdsgJr12NP2eCICspcULiWa5u9udOA="},"next_validators_hash":"E30CE736441FB9101FADDAF7E578ABBE6DFDB67207112350A9A904D554E1F5BE"},"unbonding_sequences":null,"initial_val_set":[{"pub_key":{"type":"tendermint/PubKeyEd25519","value":"dcASx5/LIKZqagJWN0frOlFtcvz91frYmj/zmoZRWro="},"power":1}]}` + // JSON string with tabs, newlines and spaces for readability + jsonString := `{ + "params": { + "enabled": true, + "blocks_per_distribution_transmission": 1000, + "ccv_timeout_period": 2419200000000000, + "transfer_timeout_period": 3600000000000, + "consumer_redistribution_fraction": "0.75", + "historical_entries": 10000, + "unbonding_period": 1728000000000000, + "soft_opt_out_threshold": "0.05", + "reward_denoms": [], + "provider_reward_denoms": [] + }, + "new_chain": true, + "provider_client_state": { + "chain_id": "testchain1", + "trust_level": { + "numerator": 1, + "denominator": 3 + }, + "trusting_period": 1197504000000000, + "unbonding_period": 1814400000000000, + "max_clock_drift": 10000000000, + "frozen_height": {}, + "latest_height": { + "revision_height": 5 + }, + "proof_specs": [ + { + "leaf_spec": { + "hash": 1, + "prehash_value": 1, + "length": 1, + "prefix": "AA==" + }, + "inner_spec": { + "child_order": [0, 1], + "child_size": 33, + "min_prefix_length": 4, + "max_prefix_length": 12, + "hash": 1 + } + }, + { + "leaf_spec": { + "hash": 1, + "prehash_value": 1, + "length": 1, + "prefix": "AA==" + }, + "inner_spec": { + "child_order": [0, 1], + "child_size": 32, + "min_prefix_length": 1, + "max_prefix_length": 1, + "hash": 1 + } + } + ], + "upgrade_path": ["upgrade", "upgradedIBCState"], + "allow_update_after_expiry": true, + "allow_update_after_misbehaviour": true + }, + "provider_consensus_state": { + "timestamp": "2020-01-02T00:00:10Z", + "root": { + "hash": "LpGpeyQVLUo9HpdsgJr12NP2eCICspcULiWa5u9udOA=" + }, + "next_validators_hash": "E30CE736441FB9101FADDAF7E578ABBE6DFDB67207112350A9A904D554E1F5BE" + }, + "unbonding_sequences": null, + "initial_val_set": [ + { + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "dcASx5/LIKZqagJWN0frOlFtcvz91frYmj/zmoZRWro=" + }, + "power": 1 + } + ] + }` var expectedGenesis consumertypes.GenesisState - err = json.Unmarshal([]byte(jsonString), &expectedGenesis) + err = json.Unmarshal([]byte(jsonString), &expectedGenesis) // ignores tabs, newlines and spaces require.NoError(t, err) // Zeroing out different fields that are challenging to mock From 064c601231b85066bfaf734efec2751a672528a7 Mon Sep 17 00:00:00 2001 From: yaruwangway <69694322+yaruwangway@users.noreply.github.com> Date: Wed, 19 Jul 2023 17:29:59 +0200 Subject: [PATCH 25/38] Fix: export InitTimeoutTimestamps and VscSendTimestamps to genesis (#1076) * fix: add InitTimeoutTimestamps and VscSendTimestamps to genesis * fix: add chainID to vscSendTimestamp * test: fix tests * test: fixgenesis test * test: fix test TestInitAndExportGenesis * feat: add exportedVscSendTimestamps * docs: update changelog * feat: update exportedVscSendTimestamps * fix: lint --- CHANGELOG.md | 1 + .../ccv/consumer/v1/genesis.proto | 2 +- .../ccv/provider/v1/genesis.proto | 6 + .../ccv/provider/v1/provider.proto | 7 + .../ccv/provider/v1/tx.proto | 2 +- x/ccv/provider/keeper/genesis.go | 15 + x/ccv/provider/keeper/genesis_test.go | 47 ++ x/ccv/provider/module_test.go | 2 + x/ccv/provider/types/genesis.go | 4 + x/ccv/provider/types/genesis.pb.go | 241 +++++++--- x/ccv/provider/types/genesis_test.go | 58 +++ x/ccv/provider/types/provider.pb.go | 446 +++++++++++++----- 12 files changed, 668 insertions(+), 163 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a5c14c6e4..818bdca374 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] Add an entry to the unreleased section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a release. +* `[x/ccv/provider]` (fix) [#1076](https://github.com/cosmos/interchain-security/pull/1076) Add `InitTimeoutTimestamps` and `ExportedVscSendTimestamps` to exported genesis. * (fix!) revert consumer packet data changes from #1037 [#1150](https://github.com/cosmos/interchain-security/pull/1150) * (fix!) proper deletion of pending packets [#1146](https://github.com/cosmos/interchain-security/pull/1146) diff --git a/proto/interchain_security/ccv/consumer/v1/genesis.proto b/proto/interchain_security/ccv/consumer/v1/genesis.proto index 9c9418ef3c..3511f3f349 100644 --- a/proto/interchain_security/ccv/consumer/v1/genesis.proto +++ b/proto/interchain_security/ccv/consumer/v1/genesis.proto @@ -55,4 +55,4 @@ message HeightToValsetUpdateID { // OutstandingDowntime defines the genesis information for each validator // flagged with an outstanding downtime slashing. -message OutstandingDowntime { string validator_consensus_address = 1; } \ No newline at end of file +message OutstandingDowntime { string validator_consensus_address = 1; } diff --git a/proto/interchain_security/ccv/provider/v1/genesis.proto b/proto/interchain_security/ccv/provider/v1/genesis.proto index e1f6ab2e1a..1bc412262c 100644 --- a/proto/interchain_security/ccv/provider/v1/genesis.proto +++ b/proto/interchain_security/ccv/provider/v1/genesis.proto @@ -44,6 +44,12 @@ message GenesisState { // empty for a new chain repeated ConsumerAddrsToPrune consumer_addrs_to_prune = 11 [ (gogoproto.nullable) = false ]; + + repeated interchain_security.ccv.provider.v1.InitTimeoutTimestamp init_timeout_timestamps = 12 + [ (gogoproto.nullable) = false ]; + + repeated interchain_security.ccv.provider.v1.ExportedVscSendTimestamp exported_vsc_send_timestamps = 13 + [ (gogoproto.nullable) = false ]; } // consumer chain diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index b986255ce5..3c41e01c89 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -221,6 +221,13 @@ message VscSendTimestamp { [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; } +// ExportedVscSendTimestamps is VscSendTimestamp with chainID info for exporting to genesis +message ExportedVscSendTimestamp { + string chain_id = 1; + repeated VscSendTimestamp vsc_send_timestamps = 2 + [ (gogoproto.nullable) = false ]; +} + // // Key assignment section // diff --git a/proto/interchain_security/ccv/provider/v1/tx.proto b/proto/interchain_security/ccv/provider/v1/tx.proto index 257ab6e4e4..952ba31c09 100644 --- a/proto/interchain_security/ccv/provider/v1/tx.proto +++ b/proto/interchain_security/ccv/provider/v1/tx.proto @@ -44,4 +44,4 @@ message MsgRegisterConsumerRewardDenom { // MsgRegisterConsumerRewardDenomResponse defines the // Msg/RegisterConsumerRewardDenom response type. -message MsgRegisterConsumerRewardDenomResponse {} \ No newline at end of file +message MsgRegisterConsumerRewardDenomResponse {} diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index b3a0a1ef04..f201e6de50 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -91,6 +91,16 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { } } + for _, item := range genState.InitTimeoutTimestamps { + k.SetInitTimeoutTimestamp(ctx, item.ChainId, item.Timestamp) + } + + for _, item := range genState.ExportedVscSendTimestamps { + for _, vscSendTimestamp := range item.VscSendTimestamps { + k.SetVscSendTimestamp(ctx, item.ChainId, vscSendTimestamp.VscId, vscSendTimestamp.Timestamp) + } + } + k.SetParams(ctx, genState.Params) k.InitializeSlashMeter(ctx) } @@ -100,6 +110,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { // get a list of all registered consumer chains registeredChains := k.GetAllConsumerChains(ctx) + var exportedVscSendTimestamps []types.ExportedVscSendTimestamp // export states for each consumer chains var consumerStates []types.ConsumerState for _, chain := range registeredChains { @@ -130,6 +141,8 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { cs.PendingValsetChanges = k.GetPendingVSCPackets(ctx, chain.ChainId) consumerStates = append(consumerStates, cs) + vscSendTimestamps := k.GetAllVscSendTimestamps(ctx, chain.ChainId) + exportedVscSendTimestamps = append(exportedVscSendTimestamps, types.ExportedVscSendTimestamp{ChainId: chain.ChainId, VscSendTimestamps: vscSendTimestamps}) } // ConsumerAddrsToPrune are added only for registered consumer chains @@ -152,5 +165,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { k.GetAllValidatorConsumerPubKeys(ctx, nil), k.GetAllValidatorsByConsumerAddr(ctx, nil), consumerAddrsToPrune, + k.GetAllInitTimeoutTimestamps(ctx), + exportedVscSendTimestamps, ) } diff --git a/x/ccv/provider/keeper/genesis_test.go b/x/ccv/provider/keeper/genesis_test.go index d9147ce98f..01a3845651 100644 --- a/x/ccv/provider/keeper/genesis_test.go +++ b/x/ccv/provider/keeper/genesis_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "sort" "testing" "time" @@ -36,6 +37,32 @@ func TestInitAndExportGenesis(t *testing.T) { consumerTmPubKey := consumerCryptoId.TMProtoCryptoPublicKey() consumerConsAddr := consumerCryptoId.ConsumerConsAddress() + initTimeoutTimeStamps := []providertypes.InitTimeoutTimestamp{ + {ChainId: cChainIDs[0], Timestamp: uint64(time.Now().UTC().UnixNano()) + 10}, + {ChainId: cChainIDs[1], Timestamp: uint64(time.Now().UTC().UnixNano()) + 15}, + } + + now := time.Now().UTC() + exportedVscSendTimeStampsC0 := providertypes.ExportedVscSendTimestamp{ + ChainId: "c0", + VscSendTimestamps: []providertypes.VscSendTimestamp{ + {VscId: 1, Timestamp: now.Add(time.Hour)}, + {VscId: 2, Timestamp: now.Add(2 * time.Hour)}, + }, + } + + exportedVscSendTimeStampsC1 := providertypes.ExportedVscSendTimestamp{ + ChainId: "c1", + VscSendTimestamps: []providertypes.VscSendTimestamp{ + {VscId: 1, Timestamp: now.Add(-time.Hour)}, + {VscId: 2, Timestamp: now.Add(time.Hour)}, + }, + } + + var exportedVscSendTimeStampsAll []providertypes.ExportedVscSendTimestamp + exportedVscSendTimeStampsAll = append(exportedVscSendTimeStampsAll, exportedVscSendTimeStampsC0) + exportedVscSendTimeStampsAll = append(exportedVscSendTimeStampsAll, exportedVscSendTimeStampsC1) + // create genesis struct provGenesis := providertypes.NewGenesisState(vscID, []providertypes.ValsetUpdateIdToHeight{{ValsetUpdateId: vscID, Height: initHeight}}, @@ -98,6 +125,8 @@ func TestInitAndExportGenesis(t *testing.T) { ConsumerAddrs: &providertypes.AddressList{Addresses: [][]byte{consumerConsAddr.ToSdkConsAddr()}}, }, }, + initTimeoutTimeStamps, + exportedVscSendTimeStampsAll, ) // Instantiate in-mem provider keeper with mocks @@ -164,6 +193,24 @@ func TestInitAndExportGenesis(t *testing.T) { // check the exported genesis require.Equal(t, provGenesis, pk.ExportGenesis(ctx)) + + initTimeoutTimestampInStore := pk.GetAllInitTimeoutTimestamps(ctx) + sort.Slice(initTimeoutTimestampInStore, func(i, j int) bool { + return initTimeoutTimestampInStore[i].Timestamp < initTimeoutTimestampInStore[j].Timestamp + }) + require.Equal(t, initTimeoutTimestampInStore, initTimeoutTimeStamps) + + vscSendTimestampsC0InStore := pk.GetAllVscSendTimestamps(ctx, cChainIDs[0]) + sort.Slice(vscSendTimestampsC0InStore, func(i, j int) bool { + return vscSendTimestampsC0InStore[i].VscId < vscSendTimestampsC0InStore[j].VscId + }) + require.Equal(t, vscSendTimestampsC0InStore, exportedVscSendTimeStampsC0.VscSendTimestamps) + + vscSendTimestampsC1InStore := pk.GetAllVscSendTimestamps(ctx, cChainIDs[1]) + sort.Slice(vscSendTimestampsC1InStore, func(i, j int) bool { + return vscSendTimestampsC1InStore[i].VscId < vscSendTimestampsC1InStore[j].VscId + }) + require.Equal(t, vscSendTimestampsC1InStore, exportedVscSendTimeStampsC1.VscSendTimestamps) } func assertConsumerChainStates(t *testing.T, ctx sdk.Context, pk keeper.Keeper, consumerStates ...providertypes.ConsumerState) { diff --git a/x/ccv/provider/module_test.go b/x/ccv/provider/module_test.go index f32cb8cc4d..bf63f86b40 100644 --- a/x/ccv/provider/module_test.go +++ b/x/ccv/provider/module_test.go @@ -108,6 +108,8 @@ func TestInitGenesis(t *testing.T) { nil, nil, nil, + nil, + nil, ) cdc := keeperParams.Cdc diff --git a/x/ccv/provider/types/genesis.go b/x/ccv/provider/types/genesis.go index 07c135ebfc..2118f3143e 100644 --- a/x/ccv/provider/types/genesis.go +++ b/x/ccv/provider/types/genesis.go @@ -24,6 +24,8 @@ func NewGenesisState( validatorConsumerPubkeys []ValidatorConsumerPubKey, validatorsByConsumerAddr []ValidatorByConsumerAddr, consumerAddrsToPrune []ConsumerAddrsToPrune, + initTimeoutTimestamps []InitTimeoutTimestamp, + exportedVscSendTimestamps []ExportedVscSendTimestamp, ) *GenesisState { return &GenesisState{ ValsetUpdateId: vscID, @@ -37,6 +39,8 @@ func NewGenesisState( ValidatorConsumerPubkeys: validatorConsumerPubkeys, ValidatorsByConsumerAddr: validatorsByConsumerAddr, ConsumerAddrsToPrune: consumerAddrsToPrune, + InitTimeoutTimestamps: initTimeoutTimestamps, + ExportedVscSendTimestamps: exportedVscSendTimestamps, } } diff --git a/x/ccv/provider/types/genesis.pb.go b/x/ccv/provider/types/genesis.pb.go index 0e4e4f2cf3..ddd7d478b6 100644 --- a/x/ccv/provider/types/genesis.pb.go +++ b/x/ccv/provider/types/genesis.pb.go @@ -48,7 +48,9 @@ type GenesisState struct { // empty for a new chain ValidatorsByConsumerAddr []ValidatorByConsumerAddr `protobuf:"bytes,10,rep,name=validators_by_consumer_addr,json=validatorsByConsumerAddr,proto3" json:"validators_by_consumer_addr"` // empty for a new chain - ConsumerAddrsToPrune []ConsumerAddrsToPrune `protobuf:"bytes,11,rep,name=consumer_addrs_to_prune,json=consumerAddrsToPrune,proto3" json:"consumer_addrs_to_prune"` + ConsumerAddrsToPrune []ConsumerAddrsToPrune `protobuf:"bytes,11,rep,name=consumer_addrs_to_prune,json=consumerAddrsToPrune,proto3" json:"consumer_addrs_to_prune"` + InitTimeoutTimestamps []InitTimeoutTimestamp `protobuf:"bytes,12,rep,name=init_timeout_timestamps,json=initTimeoutTimestamps,proto3" json:"init_timeout_timestamps"` + ExportedVscSendTimestamps []ExportedVscSendTimestamp `protobuf:"bytes,13,rep,name=exported_vsc_send_timestamps,json=exportedVscSendTimestamps,proto3" json:"exported_vsc_send_timestamps"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -161,6 +163,20 @@ func (m *GenesisState) GetConsumerAddrsToPrune() []ConsumerAddrsToPrune { return nil } +func (m *GenesisState) GetInitTimeoutTimestamps() []InitTimeoutTimestamp { + if m != nil { + return m.InitTimeoutTimestamps + } + return nil +} + +func (m *GenesisState) GetExportedVscSendTimestamps() []ExportedVscSendTimestamp { + if m != nil { + return m.ExportedVscSendTimestamps + } + return nil +} + // consumer chain type ConsumerState struct { // ChainID defines the chain ID for the consumer chain @@ -336,61 +352,66 @@ func init() { } var fileDescriptor_48411d9c7900d48e = []byte{ - // 856 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x8e, 0xdb, 0x44, - 0x14, 0x5e, 0xef, 0xa6, 0xdb, 0xcd, 0x6c, 0x77, 0x59, 0x86, 0x55, 0x70, 0xb3, 0x90, 0xae, 0x02, - 0x48, 0x91, 0x00, 0x1b, 0xa7, 0x5c, 0xf0, 0xd7, 0x8b, 0xa6, 0x95, 0x20, 0x42, 0x88, 0x28, 0xfd, - 0x41, 0x2a, 0x17, 0xd6, 0x64, 0x3c, 0x4a, 0x86, 0xd8, 0x33, 0xd6, 0xcc, 0xd8, 0xd4, 0x42, 0x48, - 0x54, 0xbc, 0x00, 0x6f, 0x45, 0x2f, 0x7b, 0xc9, 0x55, 0x85, 0x76, 0xdf, 0x80, 0x27, 0x40, 0x1e, - 0x8f, 0x5d, 0x7b, 0x49, 0x20, 0xe1, 0x2e, 0x3e, 0xdf, 0x9c, 0xef, 0x3b, 0x67, 0xce, 0xcc, 0x37, - 0x01, 0x1e, 0x65, 0x8a, 0x08, 0xbc, 0x40, 0x94, 0xf9, 0x92, 0xe0, 0x44, 0x50, 0x95, 0xb9, 0x18, - 0xa7, 0x6e, 0x2c, 0x78, 0x4a, 0x03, 0x22, 0xdc, 0xd4, 0x73, 0xe7, 0x84, 0x11, 0x49, 0xa5, 0x13, - 0x0b, 0xae, 0x38, 0x7c, 0x67, 0x45, 0x8a, 0x83, 0x71, 0xea, 0x94, 0x29, 0x4e, 0xea, 0x75, 0x4f, - 0xe7, 0x7c, 0xce, 0xf5, 0x7a, 0x37, 0xff, 0x55, 0xa4, 0x76, 0xdf, 0x5d, 0xa7, 0x96, 0x7a, 0xae, - 0x61, 0x50, 0xbc, 0x3b, 0xdc, 0xa4, 0xa6, 0x4a, 0xec, 0x3f, 0x72, 0x30, 0x67, 0x32, 0x89, 0x8a, - 0x9c, 0xf2, 0xb7, 0xc9, 0xf1, 0x36, 0xc9, 0x69, 0xf4, 0xde, 0x7d, 0x4b, 0x11, 0x16, 0x10, 0x11, - 0x51, 0xa6, 0x5c, 0x2c, 0xb2, 0x58, 0x71, 0x77, 0x49, 0x32, 0x83, 0xf6, 0x7f, 0x6f, 0x83, 0x1b, - 0x5f, 0x16, 0xeb, 0x1f, 0x28, 0xa4, 0x08, 0x1c, 0x80, 0x93, 0x14, 0x85, 0x92, 0x28, 0x3f, 0x89, - 0x03, 0xa4, 0x88, 0x4f, 0x03, 0xdb, 0x3a, 0xb7, 0x06, 0xad, 0xe9, 0x71, 0x11, 0x7f, 0xa4, 0xc3, - 0xe3, 0x00, 0xfe, 0x04, 0x5e, 0x2b, 0x55, 0x7d, 0x99, 0xe7, 0x4a, 0x7b, 0xf7, 0x7c, 0x6f, 0x70, - 0x38, 0x1c, 0x3a, 0x1b, 0x6c, 0xb7, 0x73, 0xcf, 0xe4, 0x6a, 0xd9, 0x51, 0xef, 0xf9, 0xcb, 0x5b, - 0x3b, 0x7f, 0xbd, 0xbc, 0xd5, 0xc9, 0x50, 0x14, 0x7e, 0xd6, 0xbf, 0x42, 0xdc, 0x9f, 0x1e, 0xe3, - 0xfa, 0x72, 0x09, 0xbf, 0x07, 0x47, 0x09, 0x9b, 0x71, 0x16, 0x50, 0x36, 0xf7, 0x79, 0x2c, 0xed, - 0x3d, 0x2d, 0xfd, 0xd1, 0x46, 0xd2, 0x8f, 0xca, 0xcc, 0x6f, 0xe3, 0x51, 0x2b, 0x17, 0x9e, 0xde, - 0x48, 0x5e, 0x85, 0x24, 0x44, 0xe0, 0x34, 0x42, 0x2a, 0x11, 0xc4, 0x6f, 0x6a, 0xb4, 0xce, 0xad, - 0xc1, 0xe1, 0xd0, 0x5d, 0xab, 0x91, 0x7a, 0xce, 0x37, 0x3a, 0x2f, 0xa8, 0x29, 0xc8, 0x29, 0x2c, - 0xc8, 0xea, 0x31, 0xf8, 0x33, 0xe8, 0x5e, 0xdd, 0x66, 0x5f, 0x71, 0x7f, 0x41, 0xe8, 0x7c, 0xa1, - 0xec, 0x6b, 0xba, 0x99, 0xcf, 0x37, 0x6a, 0xe6, 0x71, 0x63, 0x2a, 0x0f, 0xf9, 0x57, 0x9a, 0xc2, - 0xf4, 0xd5, 0x49, 0x57, 0xa2, 0xf0, 0x57, 0x0b, 0x9c, 0x55, 0x7b, 0x8c, 0x82, 0x80, 0x2a, 0xca, - 0x99, 0x1f, 0x0b, 0x1e, 0x73, 0x89, 0x42, 0x69, 0xef, 0xeb, 0x02, 0xee, 0x6c, 0x35, 0xc8, 0xbb, - 0x86, 0x66, 0x62, 0x58, 0x4c, 0x09, 0x37, 0xf1, 0x1a, 0x5c, 0xc2, 0x5f, 0x2c, 0xd0, 0xad, 0xaa, - 0x10, 0x24, 0xe2, 0x29, 0x0a, 0x6b, 0x45, 0x5c, 0xd7, 0x45, 0x7c, 0xb1, 0x55, 0x11, 0xd3, 0x82, - 0xe5, 0x4a, 0x0d, 0x36, 0x5e, 0x0d, 0x4b, 0x38, 0x06, 0xfb, 0x31, 0x12, 0x28, 0x92, 0xf6, 0x81, - 0x1e, 0xee, 0xfb, 0x1b, 0xa9, 0x4d, 0x74, 0x8a, 0x21, 0x37, 0x04, 0xba, 0x9b, 0x14, 0x85, 0x34, - 0x40, 0x8a, 0x0b, 0xbf, 0xea, 0x2b, 0x4e, 0x66, 0xf9, 0x7d, 0xb3, 0xdb, 0x5b, 0x74, 0xf3, 0xb8, - 0xa4, 0x29, 0xdb, 0x9a, 0x24, 0xb3, 0xaf, 0x49, 0x56, 0x76, 0x93, 0xae, 0x80, 0x73, 0x0d, 0xf8, - 0xcc, 0x02, 0x67, 0x15, 0x28, 0xfd, 0x59, 0xe6, 0xd7, 0x87, 0x2c, 0x6c, 0xf0, 0x7f, 0x6a, 0x18, - 0x65, 0xb5, 0x09, 0x8b, 0x7f, 0xd4, 0x20, 0x9b, 0x38, 0x4c, 0xc1, 0x9b, 0x0d, 0x51, 0x99, 0x9f, - 0xeb, 0x58, 0x24, 0x8c, 0xd8, 0x87, 0x5a, 0xfe, 0xd3, 0x6d, 0x4f, 0x95, 0x90, 0x0f, 0xf9, 0x24, - 0x27, 0x30, 0xda, 0xa7, 0x78, 0x05, 0xd6, 0x7f, 0xd6, 0x02, 0x47, 0x0d, 0x4f, 0x81, 0x37, 0xc1, - 0x41, 0x21, 0x62, 0x2c, 0xac, 0x3d, 0xbd, 0xae, 0xbf, 0xc7, 0x01, 0x7c, 0x1b, 0x00, 0xbc, 0x40, - 0x8c, 0x91, 0x30, 0x07, 0x77, 0x35, 0xd8, 0x36, 0x91, 0x71, 0x00, 0xcf, 0x40, 0x1b, 0x87, 0x94, - 0x30, 0x95, 0xa3, 0x7b, 0x1a, 0x3d, 0x28, 0x02, 0xe3, 0x00, 0xbe, 0x07, 0x8e, 0x29, 0xa3, 0x8a, - 0xa2, 0xb0, 0xbc, 0xae, 0x2d, 0xed, 0x8f, 0x47, 0x26, 0x6a, 0xae, 0xd8, 0x0c, 0x9c, 0x54, 0xfb, - 0x60, 0x1c, 0xd9, 0xbe, 0xa6, 0xcf, 0x98, 0xb7, 0x76, 0x03, 0x2a, 0xb7, 0x4f, 0x3d, 0xa7, 0xee, - 0xca, 0xa6, 0xf1, 0xca, 0x6f, 0x0d, 0x06, 0x15, 0xe8, 0xc4, 0xa4, 0xf0, 0x27, 0xe3, 0x26, 0x79, - 0x0f, 0x73, 0x52, 0x5e, 0xe0, 0x4f, 0xfe, 0xcd, 0xaa, 0xaa, 0x01, 0x3f, 0x20, 0xea, 0x9e, 0x4e, - 0x9b, 0x20, 0xbc, 0x24, 0xea, 0x3e, 0x52, 0xa8, 0xdc, 0x69, 0xc3, 0x5e, 0x78, 0x4c, 0xb1, 0x48, - 0xc2, 0x0f, 0x00, 0x94, 0x21, 0x92, 0x0b, 0x3f, 0xe0, 0x3f, 0x32, 0x45, 0x23, 0xe2, 0x23, 0xbc, - 0xd4, 0xb7, 0xb5, 0x3d, 0x3d, 0xd1, 0xc8, 0x7d, 0x03, 0xdc, 0xc5, 0x4b, 0xf8, 0x03, 0x78, 0xa3, - 0xe1, 0xa2, 0x3e, 0x65, 0x01, 0x79, 0x6a, 0x1f, 0xe8, 0x02, 0x3f, 0xde, 0xec, 0x28, 0x4a, 0x5c, - 0x37, 0x4f, 0x53, 0xdc, 0xeb, 0x75, 0xcf, 0x1e, 0xe7, 0xa4, 0xfd, 0x27, 0xa0, 0xb3, 0xda, 0x0e, - 0xb7, 0x78, 0xd6, 0x3a, 0x60, 0xdf, 0x8c, 0x75, 0x57, 0xe3, 0xe6, 0x6b, 0xf4, 0xdd, 0xf3, 0x8b, - 0x9e, 0xf5, 0xe2, 0xa2, 0x67, 0xfd, 0x79, 0xd1, 0xb3, 0x7e, 0xbb, 0xec, 0xed, 0xbc, 0xb8, 0xec, - 0xed, 0xfc, 0x71, 0xd9, 0xdb, 0x79, 0x72, 0x67, 0x4e, 0xd5, 0x22, 0x99, 0x39, 0x98, 0x47, 0x2e, - 0xe6, 0x32, 0xe2, 0xd2, 0x7d, 0xd5, 0xd5, 0x87, 0xd5, 0x33, 0x9d, 0xde, 0x76, 0x9f, 0x36, 0xff, - 0x13, 0xa8, 0x2c, 0x26, 0x72, 0xb6, 0xaf, 0x5f, 0xe2, 0xdb, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, - 0xa6, 0x98, 0xf7, 0xad, 0xd8, 0x08, 0x00, 0x00, + // 930 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x6e, 0x23, 0x35, + 0x14, 0xee, 0xb4, 0xd9, 0x6e, 0xe3, 0xfe, 0x50, 0x4c, 0xc9, 0x4e, 0xd3, 0x25, 0x5b, 0x05, 0x90, + 0x2a, 0x01, 0x33, 0xa4, 0xcb, 0x05, 0x7f, 0x7b, 0xb1, 0xdd, 0x45, 0x10, 0x21, 0x44, 0x94, 0x76, + 0x8b, 0xb4, 0x5c, 0x58, 0x8e, 0x6d, 0x25, 0xa6, 0x33, 0xf6, 0x68, 0xec, 0x99, 0x6d, 0x84, 0x90, + 0x58, 0xc1, 0x03, 0xf0, 0x56, 0xec, 0xe5, 0x5e, 0x72, 0xb5, 0x42, 0xed, 0x1b, 0xf0, 0x04, 0x68, + 0x3c, 0x9e, 0xe9, 0x24, 0x24, 0x90, 0x70, 0x95, 0xcc, 0xf9, 0x7c, 0xbe, 0xef, 0x1c, 0x1f, 0xfb, + 0x1c, 0x83, 0x0e, 0x17, 0x9a, 0xc5, 0x64, 0x84, 0xb9, 0x40, 0x8a, 0x91, 0x24, 0xe6, 0x7a, 0xec, + 0x13, 0x92, 0xfa, 0x51, 0x2c, 0x53, 0x4e, 0x59, 0xec, 0xa7, 0x1d, 0x7f, 0xc8, 0x04, 0x53, 0x5c, + 0x79, 0x51, 0x2c, 0xb5, 0x84, 0x6f, 0xcf, 0x70, 0xf1, 0x08, 0x49, 0xbd, 0xc2, 0xc5, 0x4b, 0x3b, + 0xcd, 0xbd, 0xa1, 0x1c, 0x4a, 0xb3, 0xde, 0xcf, 0xfe, 0xe5, 0xae, 0xcd, 0x77, 0xe6, 0xa9, 0xa5, + 0x1d, 0xdf, 0x32, 0x68, 0xd9, 0x3c, 0x5e, 0x24, 0xa6, 0x52, 0xec, 0x3f, 0x7c, 0x88, 0x14, 0x2a, + 0x09, 0x73, 0x9f, 0xe2, 0xbf, 0xf5, 0xe9, 0x2c, 0xe2, 0x33, 0x91, 0x7b, 0xf3, 0xae, 0x66, 0x82, + 0xb2, 0x38, 0xe4, 0x42, 0xfb, 0x24, 0x1e, 0x47, 0x5a, 0xfa, 0x17, 0x6c, 0x6c, 0xd1, 0xf6, 0xef, + 0x9b, 0x60, 0xeb, 0xcb, 0x7c, 0xfd, 0xa9, 0xc6, 0x9a, 0xc1, 0x23, 0xb0, 0x9b, 0xe2, 0x40, 0x31, + 0x8d, 0x92, 0x88, 0x62, 0xcd, 0x10, 0xa7, 0xae, 0x73, 0xe8, 0x1c, 0xd5, 0xfa, 0x3b, 0xb9, 0xfd, + 0x89, 0x31, 0x77, 0x29, 0xfc, 0x11, 0xbc, 0x56, 0xa8, 0x22, 0x95, 0xf9, 0x2a, 0x77, 0xf5, 0x70, + 0xed, 0x68, 0xf3, 0xf8, 0xd8, 0x5b, 0x60, 0xbb, 0xbd, 0x47, 0xd6, 0xd7, 0xc8, 0x9e, 0xb4, 0x5e, + 0xbc, 0xba, 0xb7, 0xf2, 0xd7, 0xab, 0x7b, 0x8d, 0x31, 0x0e, 0x83, 0x4f, 0xdb, 0x53, 0xc4, 0xed, + 0xfe, 0x0e, 0xa9, 0x2e, 0x57, 0xf0, 0x7b, 0xb0, 0x9d, 0x88, 0x81, 0x14, 0x94, 0x8b, 0x21, 0x92, + 0x91, 0x72, 0xd7, 0x8c, 0xf4, 0x87, 0x0b, 0x49, 0x3f, 0x29, 0x3c, 0xbf, 0x8d, 0x4e, 0x6a, 0x99, + 0x70, 0x7f, 0x2b, 0xb9, 0x31, 0x29, 0x88, 0xc1, 0x5e, 0x88, 0x75, 0x12, 0x33, 0x34, 0xa9, 0x51, + 0x3b, 0x74, 0x8e, 0x36, 0x8f, 0xfd, 0xb9, 0x1a, 0x69, 0xc7, 0xfb, 0xc6, 0xf8, 0xd1, 0x8a, 0x82, + 0xea, 0xc3, 0x9c, 0xac, 0x6a, 0x83, 0x3f, 0x81, 0xe6, 0xf4, 0x36, 0x23, 0x2d, 0xd1, 0x88, 0xf1, + 0xe1, 0x48, 0xbb, 0xb7, 0x4c, 0x32, 0x9f, 0x2d, 0x94, 0xcc, 0xf9, 0x44, 0x55, 0xce, 0xe4, 0x57, + 0x86, 0xc2, 0xe6, 0xd5, 0x48, 0x67, 0xa2, 0xf0, 0x17, 0x07, 0x1c, 0x94, 0x7b, 0x8c, 0x29, 0xe5, + 0x9a, 0x4b, 0x81, 0xa2, 0x58, 0x46, 0x52, 0xe1, 0x40, 0xb9, 0xeb, 0x26, 0x80, 0x07, 0x4b, 0x15, + 0xf2, 0xa1, 0xa5, 0xe9, 0x59, 0x16, 0x1b, 0xc2, 0x3e, 0x99, 0x83, 0x2b, 0xf8, 0xb3, 0x03, 0x9a, + 0x65, 0x14, 0x31, 0x0b, 0x65, 0x8a, 0x83, 0x4a, 0x10, 0xb7, 0x4d, 0x10, 0x9f, 0x2f, 0x15, 0x44, + 0x3f, 0x67, 0x99, 0x8a, 0xc1, 0x25, 0xb3, 0x61, 0x05, 0xbb, 0x60, 0x3d, 0xc2, 0x31, 0x0e, 0x95, + 0xbb, 0x61, 0x8a, 0xfb, 0xde, 0x42, 0x6a, 0x3d, 0xe3, 0x62, 0xc9, 0x2d, 0x81, 0xc9, 0x26, 0xc5, + 0x01, 0xa7, 0x58, 0xcb, 0x18, 0x95, 0x79, 0x45, 0xc9, 0x20, 0xbb, 0x6f, 0x6e, 0x7d, 0x89, 0x6c, + 0xce, 0x0b, 0x9a, 0x22, 0xad, 0x5e, 0x32, 0xf8, 0x9a, 0x8d, 0x8b, 0x6c, 0xd2, 0x19, 0x70, 0xa6, + 0x01, 0x9f, 0x3b, 0xe0, 0xa0, 0x04, 0x15, 0x1a, 0x8c, 0x51, 0xb5, 0xc8, 0xb1, 0x0b, 0xfe, 0x4f, + 0x0c, 0x27, 0xe3, 0x4a, 0x85, 0xe3, 0x7f, 0xc4, 0xa0, 0x26, 0x71, 0x98, 0x82, 0x3b, 0x13, 0xa2, + 0x2a, 0x3b, 0xd7, 0x51, 0x9c, 0x08, 0xe6, 0x6e, 0x1a, 0xf9, 0x4f, 0x96, 0x3d, 0x55, 0xb1, 0x3a, + 0x93, 0xbd, 0x8c, 0xc0, 0x6a, 0xef, 0x91, 0x19, 0x18, 0x7c, 0x06, 0xee, 0x70, 0xc1, 0x35, 0xd2, + 0x3c, 0x64, 0x32, 0xc9, 0x7f, 0x95, 0xc6, 0x61, 0xa4, 0xdc, 0xad, 0x25, 0x74, 0xbb, 0x82, 0xeb, + 0xb3, 0x9c, 0xe2, 0xac, 0x60, 0xb0, 0xba, 0x6f, 0xf2, 0x19, 0x98, 0x82, 0xbf, 0x3a, 0xe0, 0x2e, + 0xbb, 0x8c, 0x64, 0xac, 0x19, 0x45, 0xa9, 0x22, 0x48, 0x31, 0x41, 0xab, 0xf2, 0xdb, 0x4b, 0x5c, + 0xa6, 0x2f, 0x2c, 0xd1, 0xb9, 0x22, 0xa7, 0x4c, 0xd0, 0xe9, 0x10, 0xf6, 0xd9, 0x1c, 0x5c, 0xb5, + 0x9f, 0xd7, 0xc0, 0xf6, 0x44, 0x4f, 0x85, 0xfb, 0x60, 0x23, 0x57, 0xb3, 0x2d, 0xbc, 0xde, 0xbf, + 0x6d, 0xbe, 0xbb, 0x14, 0xbe, 0x05, 0x00, 0x19, 0x61, 0x21, 0x58, 0x90, 0x81, 0xab, 0x06, 0xac, + 0x5b, 0x4b, 0x97, 0xc2, 0x03, 0x50, 0x27, 0x01, 0x67, 0x42, 0x67, 0xe8, 0x9a, 0x41, 0x37, 0x72, + 0x43, 0x97, 0xc2, 0x77, 0xc1, 0x4e, 0xb6, 0x11, 0x1c, 0x07, 0x45, 0xbb, 0xaa, 0x99, 0xf9, 0xb0, + 0x6d, 0xad, 0xb6, 0xc5, 0x0c, 0xc0, 0x6e, 0x79, 0x0e, 0xec, 0x44, 0x72, 0x6f, 0x99, 0x3b, 0xd6, + 0x99, 0xbb, 0x13, 0xe5, 0xb4, 0x4b, 0x3b, 0x5e, 0x75, 0x2a, 0xd9, 0xec, 0xcb, 0x79, 0x63, 0x31, + 0xa8, 0x41, 0x23, 0x62, 0x79, 0x7f, 0xb6, 0xdd, 0x34, 0xcb, 0x61, 0xc8, 0x8a, 0x06, 0xf6, 0xf1, + 0xbf, 0xb5, 0xea, 0xf2, 0x80, 0x9f, 0x32, 0xfd, 0xc8, 0xb8, 0xf5, 0x30, 0xb9, 0x60, 0xfa, 0x31, + 0xd6, 0xb8, 0x38, 0x69, 0x96, 0x3d, 0xef, 0xb1, 0xf9, 0x22, 0x05, 0xdf, 0x07, 0x50, 0x05, 0x58, + 0x8d, 0x10, 0x95, 0xcf, 0x44, 0x56, 0x67, 0x84, 0xc9, 0x85, 0xe9, 0x56, 0xf5, 0xfe, 0xae, 0x41, + 0x1e, 0x5b, 0xe0, 0x21, 0xb9, 0x80, 0x3f, 0x80, 0x37, 0x26, 0xa6, 0x08, 0xe2, 0x82, 0xb2, 0x4b, + 0x77, 0xc3, 0x04, 0xf8, 0xd1, 0x62, 0x57, 0x51, 0x91, 0xea, 0xf0, 0xb0, 0xc1, 0xbd, 0x5e, 0x9d, + 0x59, 0xdd, 0x8c, 0xb4, 0xfd, 0x14, 0x34, 0x66, 0x8f, 0x83, 0x25, 0xc6, 0x7a, 0x03, 0xac, 0xdb, + 0xb2, 0xae, 0x1a, 0xdc, 0x7e, 0x9d, 0x7c, 0xf7, 0xe2, 0xaa, 0xe5, 0xbc, 0xbc, 0x6a, 0x39, 0x7f, + 0x5e, 0xb5, 0x9c, 0xdf, 0xae, 0x5b, 0x2b, 0x2f, 0xaf, 0x5b, 0x2b, 0x7f, 0x5c, 0xb7, 0x56, 0x9e, + 0x3e, 0x18, 0x72, 0x3d, 0x4a, 0x06, 0x1e, 0x91, 0xa1, 0x4f, 0xa4, 0x0a, 0xa5, 0xf2, 0x6f, 0xb2, + 0xfa, 0xa0, 0x7c, 0xa6, 0xa4, 0xf7, 0xfd, 0xcb, 0xc9, 0x37, 0x91, 0x1e, 0x47, 0x4c, 0x0d, 0xd6, + 0xcd, 0x4b, 0xe4, 0xfe, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x4d, 0xe2, 0xfe, 0x45, 0xd8, 0x09, + 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -413,6 +434,34 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ExportedVscSendTimestamps) > 0 { + for iNdEx := len(m.ExportedVscSendTimestamps) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ExportedVscSendTimestamps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6a + } + } + if len(m.InitTimeoutTimestamps) > 0 { + for iNdEx := len(m.InitTimeoutTimestamps) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.InitTimeoutTimestamps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + } if len(m.ConsumerAddrsToPrune) > 0 { for iNdEx := len(m.ConsumerAddrsToPrune) - 1; iNdEx >= 0; iNdEx-- { { @@ -758,6 +807,18 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + if len(m.InitTimeoutTimestamps) > 0 { + for _, e := range m.InitTimeoutTimestamps { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ExportedVscSendTimestamps) > 0 { + for _, e := range m.ExportedVscSendTimestamps { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } return n } @@ -1215,6 +1276,74 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitTimeoutTimestamps", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InitTimeoutTimestamps = append(m.InitTimeoutTimestamps, InitTimeoutTimestamp{}) + if err := m.InitTimeoutTimestamps[len(m.InitTimeoutTimestamps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExportedVscSendTimestamps", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExportedVscSendTimestamps = append(m.ExportedVscSendTimestamps, ExportedVscSendTimestamp{}) + if err := m.ExportedVscSendTimestamps[len(m.ExportedVscSendTimestamps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index dbff6a1c2f..c4b5002f07 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -41,6 +41,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), true, }, @@ -63,6 +65,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), true, }, @@ -82,6 +86,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), true, }, @@ -101,6 +107,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -120,6 +128,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -139,6 +149,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -164,6 +176,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -189,6 +203,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -214,6 +230,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -239,6 +257,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -264,6 +284,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -289,6 +311,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -314,6 +338,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -339,6 +365,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -356,6 +384,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -373,6 +403,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -390,6 +422,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -407,6 +441,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -427,6 +463,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -448,6 +486,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -469,6 +509,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -494,6 +536,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -519,6 +563,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -544,6 +590,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -578,6 +626,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -624,6 +674,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -644,6 +696,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -663,6 +717,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, @@ -682,6 +738,8 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, + nil, + nil, ), false, }, diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index e651a5415c..30dba16cdc 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -971,6 +971,59 @@ func (m *VscSendTimestamp) GetTimestamp() time.Time { return time.Time{} } +// ExportedVscSendTimestamps is VscSendTimestamp with chainID info for exporting to genesis +type ExportedVscSendTimestamp struct { + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + VscSendTimestamps []VscSendTimestamp `protobuf:"bytes,2,rep,name=vsc_send_timestamps,json=vscSendTimestamps,proto3" json:"vsc_send_timestamps"` +} + +func (m *ExportedVscSendTimestamp) Reset() { *m = ExportedVscSendTimestamp{} } +func (m *ExportedVscSendTimestamp) String() string { return proto.CompactTextString(m) } +func (*ExportedVscSendTimestamp) ProtoMessage() {} +func (*ExportedVscSendTimestamp) Descriptor() ([]byte, []int) { + return fileDescriptor_f22ec409a72b7b72, []int{15} +} +func (m *ExportedVscSendTimestamp) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExportedVscSendTimestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExportedVscSendTimestamp.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExportedVscSendTimestamp) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExportedVscSendTimestamp.Merge(m, src) +} +func (m *ExportedVscSendTimestamp) XXX_Size() int { + return m.Size() +} +func (m *ExportedVscSendTimestamp) XXX_DiscardUnknown() { + xxx_messageInfo_ExportedVscSendTimestamp.DiscardUnknown(m) +} + +var xxx_messageInfo_ExportedVscSendTimestamp proto.InternalMessageInfo + +func (m *ExportedVscSendTimestamp) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *ExportedVscSendTimestamp) GetVscSendTimestamps() []VscSendTimestamp { + if m != nil { + return m.VscSendTimestamps + } + return nil +} + type KeyAssignmentReplacement struct { ProviderAddr []byte `protobuf:"bytes,1,opt,name=provider_addr,json=providerAddr,proto3" json:"provider_addr,omitempty"` PrevCKey *crypto.PublicKey `protobuf:"bytes,2,opt,name=prev_c_key,json=prevCKey,proto3" json:"prev_c_key,omitempty"` @@ -981,7 +1034,7 @@ func (m *KeyAssignmentReplacement) Reset() { *m = KeyAssignmentReplaceme func (m *KeyAssignmentReplacement) String() string { return proto.CompactTextString(m) } func (*KeyAssignmentReplacement) ProtoMessage() {} func (*KeyAssignmentReplacement) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{15} + return fileDescriptor_f22ec409a72b7b72, []int{16} } func (m *KeyAssignmentReplacement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1044,7 +1097,7 @@ func (m *ValidatorConsumerPubKey) Reset() { *m = ValidatorConsumerPubKey func (m *ValidatorConsumerPubKey) String() string { return proto.CompactTextString(m) } func (*ValidatorConsumerPubKey) ProtoMessage() {} func (*ValidatorConsumerPubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{16} + return fileDescriptor_f22ec409a72b7b72, []int{17} } func (m *ValidatorConsumerPubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1107,7 +1160,7 @@ func (m *ValidatorByConsumerAddr) Reset() { *m = ValidatorByConsumerAddr func (m *ValidatorByConsumerAddr) String() string { return proto.CompactTextString(m) } func (*ValidatorByConsumerAddr) ProtoMessage() {} func (*ValidatorByConsumerAddr) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{17} + return fileDescriptor_f22ec409a72b7b72, []int{18} } func (m *ValidatorByConsumerAddr) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1169,7 +1222,7 @@ func (m *ConsumerAddrsToPrune) Reset() { *m = ConsumerAddrsToPrune{} } func (m *ConsumerAddrsToPrune) String() string { return proto.CompactTextString(m) } func (*ConsumerAddrsToPrune) ProtoMessage() {} func (*ConsumerAddrsToPrune) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{18} + return fileDescriptor_f22ec409a72b7b72, []int{19} } func (m *ConsumerAddrsToPrune) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1235,6 +1288,7 @@ func init() { proto.RegisterType((*UnbondingOp)(nil), "interchain_security.ccv.provider.v1.UnbondingOp") proto.RegisterType((*InitTimeoutTimestamp)(nil), "interchain_security.ccv.provider.v1.InitTimeoutTimestamp") proto.RegisterType((*VscSendTimestamp)(nil), "interchain_security.ccv.provider.v1.VscSendTimestamp") + proto.RegisterType((*ExportedVscSendTimestamp)(nil), "interchain_security.ccv.provider.v1.ExportedVscSendTimestamp") proto.RegisterType((*KeyAssignmentReplacement)(nil), "interchain_security.ccv.provider.v1.KeyAssignmentReplacement") proto.RegisterType((*ValidatorConsumerPubKey)(nil), "interchain_security.ccv.provider.v1.ValidatorConsumerPubKey") proto.RegisterType((*ValidatorByConsumerAddr)(nil), "interchain_security.ccv.provider.v1.ValidatorByConsumerAddr") @@ -1246,107 +1300,110 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1598 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4b, 0x73, 0xdc, 0xc6, - 0x11, 0x26, 0xb8, 0x7c, 0xed, 0x2c, 0x1f, 0x12, 0x44, 0x59, 0x4b, 0x85, 0x59, 0xae, 0xe0, 0xc4, - 0xc5, 0x94, 0xcb, 0xd8, 0x90, 0xba, 0xa4, 0x54, 0x71, 0xb9, 0xc8, 0x95, 0x65, 0xd1, 0x8c, 0xad, - 0x35, 0xc8, 0x50, 0x95, 0xe4, 0x80, 0x1a, 0x0c, 0x5a, 0xbb, 0x53, 0x04, 0x30, 0xd0, 0xcc, 0x00, - 0xd2, 0x5e, 0x72, 0xce, 0xd1, 0xb9, 0xb9, 0x92, 0x8b, 0xf3, 0x0b, 0xf2, 0x37, 0x7c, 0xf4, 0x31, - 0x27, 0x3b, 0x45, 0x1d, 0x72, 0xc8, 0x9f, 0x48, 0xcd, 0xe0, 0xcd, 0x87, 0xb3, 0xaa, 0xc4, 0x37, - 0xcc, 0x4c, 0xf7, 0xd7, 0xdd, 0xd3, 0xdd, 0x5f, 0x0f, 0xd0, 0x3e, 0x8d, 0x24, 0x70, 0x32, 0xc1, - 0x34, 0x72, 0x05, 0x90, 0x84, 0x53, 0x39, 0x1d, 0x10, 0x92, 0x0e, 0x62, 0xce, 0x52, 0xea, 0x03, - 0x1f, 0xa4, 0x7b, 0xe5, 0xb7, 0x1d, 0x73, 0x26, 0x99, 0xf9, 0xee, 0x35, 0x3a, 0x36, 0x21, 0xa9, - 0x5d, 0xca, 0xa5, 0x7b, 0xf7, 0x37, 0xc7, 0x6c, 0xcc, 0xb4, 0xfc, 0x40, 0x7d, 0x65, 0xaa, 0xf7, - 0x77, 0xc6, 0x8c, 0x8d, 0x03, 0x18, 0xe8, 0x95, 0x97, 0xbc, 0x18, 0x48, 0x1a, 0x82, 0x90, 0x38, - 0x8c, 0x73, 0x81, 0xde, 0x65, 0x01, 0x3f, 0xe1, 0x58, 0x52, 0x16, 0x15, 0x00, 0xd4, 0x23, 0x03, - 0xc2, 0x38, 0x0c, 0x48, 0x40, 0x21, 0x92, 0xca, 0xbd, 0xec, 0x2b, 0x17, 0x18, 0x28, 0x81, 0x80, - 0x8e, 0x27, 0x32, 0xdb, 0x16, 0x03, 0x09, 0x91, 0x0f, 0x3c, 0xa4, 0x99, 0x70, 0xb5, 0xca, 0x15, - 0xb6, 0x6b, 0xe7, 0x84, 0x4f, 0x63, 0xc9, 0x06, 0xe7, 0x30, 0x15, 0xf9, 0xe9, 0x7b, 0x84, 0x89, - 0x90, 0x89, 0x01, 0xa8, 0xc0, 0x22, 0x02, 0x83, 0x74, 0xcf, 0x03, 0x89, 0xf7, 0xca, 0x8d, 0xc2, - 0xef, 0x5c, 0xce, 0xc3, 0xa2, 0x92, 0x21, 0x8c, 0xe6, 0x7e, 0x5b, 0xdf, 0x2f, 0xa1, 0xee, 0x90, - 0x45, 0x22, 0x09, 0x81, 0x1f, 0xf8, 0x3e, 0x55, 0x21, 0x8d, 0x38, 0x8b, 0x99, 0xc0, 0x81, 0xb9, - 0x89, 0x16, 0x25, 0x95, 0x01, 0x74, 0x8d, 0xbe, 0xb1, 0xdb, 0x76, 0xb2, 0x85, 0xd9, 0x47, 0x1d, - 0x1f, 0x04, 0xe1, 0x34, 0x56, 0xc2, 0xdd, 0x79, 0x7d, 0x56, 0xdf, 0x32, 0xb7, 0xd0, 0x4a, 0x96, - 0x05, 0xea, 0x77, 0x5b, 0xfa, 0x78, 0x59, 0xaf, 0x8f, 0x7c, 0xf3, 0x13, 0xb4, 0x4e, 0x23, 0x2a, - 0x29, 0x0e, 0xdc, 0x09, 0xa8, 0xdb, 0xe8, 0x2e, 0xf4, 0x8d, 0xdd, 0xce, 0xfe, 0x7d, 0x9b, 0x7a, - 0xc4, 0x56, 0x17, 0x68, 0xe7, 0xd7, 0x96, 0xee, 0xd9, 0x4f, 0xb5, 0xc4, 0xe1, 0xc2, 0x37, 0xdf, - 0xed, 0xcc, 0x39, 0x6b, 0xb9, 0x5e, 0xb6, 0x69, 0x3e, 0x40, 0xab, 0x63, 0x88, 0x40, 0x50, 0xe1, - 0x4e, 0xb0, 0x98, 0x74, 0x17, 0xfb, 0xc6, 0xee, 0xaa, 0xd3, 0xc9, 0xf7, 0x9e, 0x62, 0x31, 0x31, - 0x77, 0x50, 0xc7, 0xa3, 0x11, 0xe6, 0xd3, 0x4c, 0x62, 0x49, 0x4b, 0xa0, 0x6c, 0x4b, 0x0b, 0x0c, - 0x11, 0x12, 0x31, 0x7e, 0x15, 0xb9, 0x2a, 0xdb, 0xdd, 0xe5, 0xdc, 0x91, 0x2c, 0xd3, 0x76, 0x91, - 0x69, 0xfb, 0xb4, 0x28, 0x85, 0xc3, 0x15, 0xe5, 0xc8, 0x97, 0xdf, 0xef, 0x18, 0x4e, 0x5b, 0xeb, - 0xa9, 0x13, 0xf3, 0x73, 0x74, 0x2b, 0x89, 0x3c, 0x16, 0xf9, 0x34, 0x1a, 0xbb, 0x31, 0x70, 0xca, - 0xfc, 0xee, 0x8a, 0x86, 0xda, 0xba, 0x02, 0xf5, 0x38, 0x2f, 0x9a, 0x0c, 0xe9, 0x2b, 0x85, 0xb4, - 0x51, 0x2a, 0x8f, 0xb4, 0xae, 0xf9, 0x05, 0x32, 0x09, 0x49, 0xb5, 0x4b, 0x2c, 0x91, 0x05, 0x62, - 0x7b, 0x76, 0xc4, 0x5b, 0x84, 0xa4, 0xa7, 0x99, 0x76, 0x0e, 0xf9, 0x07, 0x74, 0x4f, 0x72, 0x1c, - 0x89, 0x17, 0xc0, 0x2f, 0xe3, 0xa2, 0xd9, 0x71, 0xef, 0x16, 0x18, 0x4d, 0xf0, 0xa7, 0xa8, 0x4f, - 0xf2, 0x02, 0x72, 0x39, 0xf8, 0x54, 0x48, 0x4e, 0xbd, 0x44, 0xe9, 0xba, 0x2f, 0x38, 0x26, 0xba, - 0x46, 0x3a, 0xba, 0x08, 0x7a, 0x85, 0x9c, 0xd3, 0x10, 0x7b, 0x92, 0x4b, 0x99, 0xcf, 0xd0, 0xcf, - 0xbc, 0x80, 0x91, 0x73, 0xa1, 0x9c, 0x73, 0x1b, 0x48, 0xda, 0x74, 0x48, 0x85, 0x50, 0x68, 0xab, - 0x7d, 0x63, 0xb7, 0xe5, 0x3c, 0xc8, 0x64, 0x47, 0xc0, 0x1f, 0xd7, 0x24, 0x4f, 0x6b, 0x82, 0xe6, - 0x07, 0xc8, 0x9c, 0x50, 0x21, 0x19, 0xa7, 0x04, 0x07, 0x2e, 0x44, 0x92, 0x53, 0x10, 0xdd, 0x35, - 0xad, 0x7e, 0xbb, 0x3a, 0xf9, 0x38, 0x3b, 0x30, 0x3f, 0x45, 0x0f, 0x6e, 0x34, 0xea, 0x92, 0x09, - 0x8e, 0x22, 0x08, 0xba, 0xeb, 0x3a, 0x94, 0x1d, 0xff, 0x06, 0x9b, 0xc3, 0x4c, 0xec, 0xd1, 0xca, - 0x9f, 0xbe, 0xde, 0x99, 0xfb, 0xea, 0xeb, 0x9d, 0x39, 0xeb, 0xef, 0x06, 0xba, 0x37, 0x2c, 0x03, - 0x0f, 0x59, 0x8a, 0x83, 0x1f, 0xb3, 0xc1, 0x0e, 0x50, 0x5b, 0x48, 0x16, 0x67, 0x25, 0xbd, 0xf0, - 0x16, 0x25, 0xbd, 0xa2, 0xd4, 0xd4, 0x81, 0xf5, 0x57, 0x03, 0x6d, 0x7e, 0xfc, 0x32, 0xa1, 0x29, - 0x23, 0xf8, 0xff, 0xc2, 0x07, 0xc7, 0x68, 0x0d, 0x6a, 0x78, 0xa2, 0xdb, 0xea, 0xb7, 0x76, 0x3b, - 0xfb, 0x3f, 0xb7, 0x33, 0x72, 0xb2, 0x4b, 0xce, 0xca, 0x09, 0xca, 0xae, 0x5b, 0x77, 0x9a, 0xba, - 0xd6, 0xbf, 0x0d, 0x74, 0xeb, 0x93, 0x80, 0x79, 0x38, 0x38, 0x09, 0xb0, 0x98, 0xa8, 0xe4, 0x4d, - 0x55, 0xd4, 0x1c, 0xf2, 0xae, 0xd1, 0xde, 0xcd, 0x1c, 0xb5, 0x52, 0xd3, 0x7d, 0xfc, 0x11, 0xba, - 0x5d, 0xd6, 0x71, 0x79, 0xb9, 0x3a, 0x98, 0xc3, 0x3b, 0x17, 0xdf, 0xed, 0x6c, 0x14, 0x39, 0x1c, - 0xea, 0x8b, 0x7e, 0xec, 0x6c, 0x90, 0xc6, 0x86, 0x6f, 0xf6, 0x50, 0x87, 0x7a, 0xc4, 0x15, 0xf0, - 0xd2, 0x8d, 0x92, 0x50, 0xe7, 0x65, 0xc1, 0x69, 0x53, 0x8f, 0x9c, 0xc0, 0xcb, 0xcf, 0x93, 0xd0, - 0x7c, 0x88, 0xde, 0x29, 0x06, 0x91, 0x9b, 0xe2, 0xc0, 0x55, 0xfa, 0x2e, 0xf6, 0x7d, 0xae, 0xd3, - 0xb4, 0xea, 0xdc, 0x29, 0x4e, 0xcf, 0x70, 0xa0, 0x8c, 0x1d, 0xf8, 0x3e, 0xb7, 0xfe, 0xb5, 0x88, - 0x96, 0x46, 0x98, 0xe3, 0x50, 0x98, 0xa7, 0x68, 0x43, 0x42, 0x18, 0x07, 0x58, 0x82, 0x9b, 0x71, - 0x64, 0x1e, 0xe9, 0xfb, 0x9a, 0x3b, 0xeb, 0xb3, 0xc5, 0xae, 0x4d, 0x93, 0x74, 0xcf, 0x1e, 0xea, - 0xdd, 0x13, 0x89, 0x25, 0x38, 0xeb, 0x05, 0x46, 0xb6, 0x69, 0xfe, 0x0a, 0x75, 0x25, 0x4f, 0x84, - 0xac, 0xd8, 0xab, 0x6a, 0xdb, 0x2c, 0x95, 0xef, 0x14, 0xe7, 0x59, 0xc3, 0x97, 0xed, 0x7a, 0x3d, - 0x51, 0xb5, 0xfe, 0x17, 0xa2, 0x3a, 0x41, 0x77, 0x14, 0xcb, 0x5f, 0xc6, 0x5c, 0x98, 0x1d, 0xf3, - 0xb6, 0xd2, 0x6f, 0x82, 0x7e, 0x81, 0xcc, 0x54, 0x90, 0xcb, 0x98, 0x8b, 0x6f, 0xe1, 0x67, 0x2a, - 0x48, 0x13, 0xd2, 0x47, 0xdb, 0x42, 0x15, 0x9f, 0x1b, 0x82, 0xd4, 0xb4, 0x17, 0x07, 0x10, 0x51, - 0x31, 0x29, 0xc0, 0x97, 0x66, 0x07, 0xdf, 0xd2, 0x40, 0x9f, 0x29, 0x1c, 0xa7, 0x80, 0xc9, 0xad, - 0x0c, 0x51, 0xef, 0x7a, 0x2b, 0x65, 0x82, 0x96, 0x75, 0x82, 0x7e, 0x72, 0x0d, 0x44, 0x99, 0xa5, - 0x7d, 0x74, 0x37, 0xc4, 0xaf, 0x5d, 0x39, 0xe1, 0x4c, 0xca, 0x00, 0x7c, 0x37, 0xc6, 0xe4, 0x1c, - 0xa4, 0xd0, 0x33, 0xaa, 0xe5, 0xdc, 0x09, 0xf1, 0xeb, 0xd3, 0xe2, 0x6c, 0x94, 0x1d, 0x99, 0x02, - 0xbd, 0x57, 0xa3, 0xf4, 0x57, 0x98, 0xfb, 0xae, 0x0f, 0x11, 0x0b, 0x5d, 0x0e, 0x63, 0xc5, 0x7b, - 0x38, 0x63, 0x77, 0x80, 0x72, 0x2c, 0xe5, 0x8d, 0xac, 0x5e, 0x19, 0x65, 0x13, 0x0f, 0x19, 0x8d, - 0xf2, 0xd9, 0x6d, 0x55, 0xcc, 0xaf, 0xd0, 0x1e, 0x2b, 0x30, 0xa7, 0x86, 0xf5, 0x04, 0xc0, 0xf2, - 0xd0, 0xed, 0xa7, 0x38, 0xf2, 0xc5, 0x04, 0x9f, 0xc3, 0x67, 0x20, 0xb1, 0x8f, 0x25, 0x6e, 0xf4, - 0xcc, 0x0b, 0x00, 0x37, 0x66, 0x2c, 0xc8, 0x7a, 0x26, 0xa3, 0xa0, 0xb2, 0x67, 0x9e, 0x00, 0x8c, - 0x18, 0x0b, 0x54, 0xcf, 0x98, 0x5d, 0xb4, 0x9c, 0x02, 0x17, 0x55, 0x05, 0x17, 0x4b, 0xeb, 0x17, - 0xa8, 0xad, 0x49, 0xe3, 0x80, 0x9c, 0x0b, 0x73, 0x1b, 0xb5, 0x15, 0x12, 0x08, 0x01, 0xa2, 0x6b, - 0xf4, 0x5b, 0xbb, 0x6d, 0xa7, 0xda, 0xb0, 0x24, 0xda, 0xba, 0xe9, 0x5d, 0x24, 0xcc, 0xe7, 0x68, - 0x39, 0x06, 0x3d, 0xb4, 0xb5, 0x62, 0x67, 0xff, 0x43, 0x7b, 0x86, 0xb7, 0xa7, 0x7d, 0x13, 0xa0, - 0x53, 0xa0, 0x59, 0xbc, 0x7a, 0x8d, 0x5d, 0x9a, 0x15, 0xc2, 0x3c, 0xbb, 0x6c, 0xf4, 0xd7, 0x6f, - 0x65, 0xf4, 0x12, 0x5e, 0x65, 0xf3, 0x7d, 0xd4, 0x39, 0xc8, 0xc2, 0xfe, 0x0d, 0x15, 0xf2, 0xea, - 0xb5, 0xac, 0xd6, 0xaf, 0xe5, 0x53, 0xb4, 0x9e, 0x8f, 0xb8, 0x53, 0xa6, 0x89, 0xcf, 0xfc, 0x29, - 0x42, 0xf9, 0x6c, 0x54, 0x84, 0x99, 0xa5, 0xa5, 0x9d, 0xef, 0x1c, 0xf9, 0x8d, 0x51, 0x35, 0xdf, - 0x18, 0x55, 0x96, 0x83, 0x36, 0xce, 0x04, 0xf9, 0x6d, 0xf1, 0xfe, 0x79, 0x16, 0x0b, 0xf3, 0x2e, - 0x5a, 0x52, 0xbd, 0x9a, 0x03, 0x2d, 0x38, 0x8b, 0xa9, 0x20, 0x47, 0xbe, 0xb9, 0x5b, 0x7f, 0x63, - 0xb1, 0xd8, 0xa5, 0xbe, 0xe8, 0xce, 0xf7, 0x5b, 0xbb, 0x0b, 0xce, 0x7a, 0x52, 0xa9, 0x1f, 0xf9, - 0xc2, 0xfa, 0x1d, 0xea, 0xd4, 0x00, 0xcd, 0x75, 0x34, 0x5f, 0x62, 0xcd, 0x53, 0xdf, 0x7c, 0x84, - 0xb6, 0x2a, 0xa0, 0x26, 0xdd, 0x67, 0x88, 0x6d, 0xe7, 0x5e, 0x29, 0xd0, 0x60, 0x7c, 0x61, 0x3d, - 0x43, 0x9b, 0x47, 0x15, 0xb9, 0x94, 0xc3, 0xa4, 0x11, 0xa1, 0xd1, 0x1c, 0xc6, 0xdb, 0xa8, 0x5d, - 0xfe, 0x48, 0xe8, 0xe8, 0x17, 0x9c, 0x6a, 0xc3, 0x0a, 0xd1, 0xad, 0x33, 0x41, 0x4e, 0x20, 0xf2, - 0x2b, 0xb0, 0x1b, 0x2e, 0xe0, 0xf0, 0x32, 0xd0, 0xcc, 0x0f, 0xd5, 0xca, 0xdc, 0x9f, 0x0d, 0xd4, - 0x3d, 0x86, 0xe9, 0x81, 0x10, 0x74, 0x1c, 0x85, 0x10, 0x49, 0x45, 0x16, 0x98, 0x80, 0xfa, 0x34, - 0xdf, 0x45, 0x6b, 0x65, 0xa3, 0x95, 0xfd, 0xb5, 0xea, 0xac, 0x16, 0x9b, 0xba, 0xb1, 0x1e, 0x21, - 0x14, 0x73, 0x48, 0x5d, 0xe2, 0x9e, 0xc3, 0x34, 0x77, 0x63, 0xbb, 0x3e, 0x6b, 0xb2, 0xff, 0x14, - 0x7b, 0x94, 0x78, 0x01, 0x25, 0xc7, 0x30, 0x75, 0x56, 0x94, 0xfc, 0xf0, 0x18, 0xa6, 0xea, 0xed, - 0x10, 0xb3, 0x57, 0xc0, 0xf5, 0x80, 0x68, 0x39, 0xd9, 0xc2, 0xfa, 0x8b, 0x81, 0xee, 0x9d, 0xe1, - 0x80, 0xfa, 0x58, 0x32, 0x5e, 0xdc, 0xf7, 0x28, 0xf1, 0x94, 0xc6, 0x0f, 0xdc, 0xeb, 0x15, 0x6f, - 0xe7, 0xaf, 0xf1, 0xf6, 0x23, 0xb4, 0x5a, 0x66, 0x58, 0xf9, 0xdb, 0x9a, 0xc1, 0xdf, 0x4e, 0xa1, - 0x71, 0x0c, 0x53, 0xeb, 0x8f, 0x35, 0xdf, 0x0e, 0xa7, 0xb5, 0xe6, 0xe5, 0xff, 0xc5, 0xb7, 0xd2, - 0x6c, 0xdd, 0x37, 0x52, 0xd7, 0xbf, 0x12, 0x40, 0xeb, 0x6a, 0x00, 0xd6, 0xdf, 0x0c, 0xb4, 0x59, - 0xb7, 0x2a, 0x4e, 0xd9, 0x88, 0x27, 0x11, 0xfc, 0x90, 0xf5, 0xaa, 0x7e, 0xe6, 0xeb, 0xf5, 0xf3, - 0x1c, 0xad, 0x37, 0x9c, 0x12, 0xf9, 0x6d, 0xfc, 0x72, 0x26, 0x0a, 0xa9, 0xd1, 0x83, 0xb3, 0x56, - 0x8f, 0x43, 0x1c, 0x3e, 0xff, 0xe6, 0xa2, 0x67, 0x7c, 0x7b, 0xd1, 0x33, 0xfe, 0x79, 0xd1, 0x33, - 0xbe, 0x7c, 0xd3, 0x9b, 0xfb, 0xf6, 0x4d, 0x6f, 0xee, 0x1f, 0x6f, 0x7a, 0x73, 0xbf, 0xff, 0x70, - 0x4c, 0xe5, 0x24, 0xf1, 0x6c, 0xc2, 0xc2, 0x41, 0xfe, 0x13, 0x5a, 0xd9, 0xfa, 0xa0, 0xfc, 0xa7, - 0x4f, 0x1f, 0x0e, 0x5e, 0x37, 0x7f, 0xec, 0xe5, 0x34, 0x06, 0xe1, 0x2d, 0xe9, 0xb2, 0x7e, 0xf8, - 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x73, 0xaf, 0xd0, 0x18, 0x09, 0x10, 0x00, 0x00, + // 1645 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4d, 0x73, 0x1b, 0xc7, + 0xd1, 0xe6, 0x12, 0xfc, 0xc2, 0x80, 0x1f, 0xe2, 0x92, 0xb2, 0x96, 0x7a, 0xf9, 0x82, 0xd0, 0x3a, + 0x71, 0x31, 0xe5, 0xf2, 0x22, 0xa4, 0x2a, 0x55, 0x29, 0x55, 0x5c, 0x2e, 0x12, 0x92, 0x2c, 0x9a, + 0xb1, 0x05, 0x2f, 0x19, 0xaa, 0x92, 0x1c, 0xb6, 0x66, 0x67, 0x5b, 0xc0, 0x14, 0x77, 0x77, 0x56, + 0x33, 0x83, 0x95, 0x70, 0xc9, 0x39, 0x47, 0xe7, 0xe6, 0x4a, 0x2e, 0x4e, 0xfe, 0x40, 0xfe, 0x86, + 0x8f, 0x3e, 0xe6, 0x64, 0xa7, 0xa4, 0x43, 0x0e, 0xf9, 0x13, 0xa9, 0x99, 0xfd, 0x04, 0x48, 0x2a, + 0x50, 0x25, 0xb9, 0xed, 0xf4, 0x74, 0x3f, 0xdd, 0x3d, 0xd3, 0xfd, 0xf4, 0x00, 0xe8, 0x90, 0xc6, + 0x12, 0x38, 0x19, 0x62, 0x1a, 0x7b, 0x02, 0xc8, 0x88, 0x53, 0x39, 0xee, 0x12, 0x92, 0x76, 0x13, + 0xce, 0x52, 0x1a, 0x00, 0xef, 0xa6, 0x07, 0xe5, 0xb7, 0x93, 0x70, 0x26, 0x99, 0xf9, 0xfe, 0x35, + 0x36, 0x0e, 0x21, 0xa9, 0x53, 0xea, 0xa5, 0x07, 0x77, 0xb7, 0x07, 0x6c, 0xc0, 0xb4, 0x7e, 0x57, + 0x7d, 0x65, 0xa6, 0x77, 0xf7, 0x06, 0x8c, 0x0d, 0x42, 0xe8, 0xea, 0x95, 0x3f, 0x7a, 0xde, 0x95, + 0x34, 0x02, 0x21, 0x71, 0x94, 0xe4, 0x0a, 0xed, 0x69, 0x85, 0x60, 0xc4, 0xb1, 0xa4, 0x2c, 0x2e, + 0x00, 0xa8, 0x4f, 0xba, 0x84, 0x71, 0xe8, 0x92, 0x90, 0x42, 0x2c, 0x55, 0x78, 0xd9, 0x57, 0xae, + 0xd0, 0x55, 0x0a, 0x21, 0x1d, 0x0c, 0x65, 0x26, 0x16, 0x5d, 0x09, 0x71, 0x00, 0x3c, 0xa2, 0x99, + 0x72, 0xb5, 0xca, 0x0d, 0x76, 0x6b, 0xfb, 0x84, 0x8f, 0x13, 0xc9, 0xba, 0x97, 0x30, 0x16, 0xf9, + 0xee, 0x07, 0x84, 0x89, 0x88, 0x89, 0x2e, 0xa8, 0xc4, 0x62, 0x02, 0xdd, 0xf4, 0xc0, 0x07, 0x89, + 0x0f, 0x4a, 0x41, 0x11, 0x77, 0xae, 0xe7, 0x63, 0x51, 0xe9, 0x10, 0x46, 0xf3, 0xb8, 0xed, 0x1f, + 0x96, 0x90, 0xd5, 0x63, 0xb1, 0x18, 0x45, 0xc0, 0x8f, 0x82, 0x80, 0xaa, 0x94, 0xfa, 0x9c, 0x25, + 0x4c, 0xe0, 0xd0, 0xdc, 0x46, 0x8b, 0x92, 0xca, 0x10, 0x2c, 0xa3, 0x63, 0xec, 0x37, 0xdd, 0x6c, + 0x61, 0x76, 0x50, 0x2b, 0x00, 0x41, 0x38, 0x4d, 0x94, 0xb2, 0x35, 0xaf, 0xf7, 0xea, 0x22, 0x73, + 0x07, 0xad, 0x64, 0xb7, 0x40, 0x03, 0xab, 0xa1, 0xb7, 0x97, 0xf5, 0xfa, 0x24, 0x30, 0x3f, 0x45, + 0xeb, 0x34, 0xa6, 0x92, 0xe2, 0xd0, 0x1b, 0x82, 0x3a, 0x0d, 0x6b, 0xa1, 0x63, 0xec, 0xb7, 0x0e, + 0xef, 0x3a, 0xd4, 0x27, 0x8e, 0x3a, 0x40, 0x27, 0x3f, 0xb6, 0xf4, 0xc0, 0x79, 0xa2, 0x35, 0x8e, + 0x17, 0xbe, 0xfd, 0x7e, 0x6f, 0xce, 0x5d, 0xcb, 0xed, 0x32, 0xa1, 0x79, 0x0f, 0xad, 0x0e, 0x20, + 0x06, 0x41, 0x85, 0x37, 0xc4, 0x62, 0x68, 0x2d, 0x76, 0x8c, 0xfd, 0x55, 0xb7, 0x95, 0xcb, 0x9e, + 0x60, 0x31, 0x34, 0xf7, 0x50, 0xcb, 0xa7, 0x31, 0xe6, 0xe3, 0x4c, 0x63, 0x49, 0x6b, 0xa0, 0x4c, + 0xa4, 0x15, 0x7a, 0x08, 0x89, 0x04, 0xbf, 0x8c, 0x3d, 0x75, 0xdb, 0xd6, 0x72, 0x1e, 0x48, 0x76, + 0xd3, 0x4e, 0x71, 0xd3, 0xce, 0x79, 0x51, 0x0a, 0xc7, 0x2b, 0x2a, 0x90, 0xaf, 0x7e, 0xd8, 0x33, + 0xdc, 0xa6, 0xb6, 0x53, 0x3b, 0xe6, 0x17, 0xe8, 0xd6, 0x28, 0xf6, 0x59, 0x1c, 0xd0, 0x78, 0xe0, + 0x25, 0xc0, 0x29, 0x0b, 0xac, 0x15, 0x0d, 0xb5, 0x73, 0x05, 0xea, 0x61, 0x5e, 0x34, 0x19, 0xd2, + 0xd7, 0x0a, 0x69, 0xa3, 0x34, 0xee, 0x6b, 0x5b, 0xf3, 0x4b, 0x64, 0x12, 0x92, 0xea, 0x90, 0xd8, + 0x48, 0x16, 0x88, 0xcd, 0xd9, 0x11, 0x6f, 0x11, 0x92, 0x9e, 0x67, 0xd6, 0x39, 0xe4, 0x6f, 0xd1, + 0x1d, 0xc9, 0x71, 0x2c, 0x9e, 0x03, 0x9f, 0xc6, 0x45, 0xb3, 0xe3, 0xde, 0x2e, 0x30, 0x26, 0xc1, + 0x9f, 0xa0, 0x0e, 0xc9, 0x0b, 0xc8, 0xe3, 0x10, 0x50, 0x21, 0x39, 0xf5, 0x47, 0xca, 0xd6, 0x7b, + 0xce, 0x31, 0xd1, 0x35, 0xd2, 0xd2, 0x45, 0xd0, 0x2e, 0xf4, 0xdc, 0x09, 0xb5, 0xc7, 0xb9, 0x96, + 0xf9, 0x14, 0xfd, 0xc8, 0x0f, 0x19, 0xb9, 0x14, 0x2a, 0x38, 0x6f, 0x02, 0x49, 0xbb, 0x8e, 0xa8, + 0x10, 0x0a, 0x6d, 0xb5, 0x63, 0xec, 0x37, 0xdc, 0x7b, 0x99, 0x6e, 0x1f, 0xf8, 0xc3, 0x9a, 0xe6, + 0x79, 0x4d, 0xd1, 0xfc, 0x08, 0x99, 0x43, 0x2a, 0x24, 0xe3, 0x94, 0xe0, 0xd0, 0x83, 0x58, 0x72, + 0x0a, 0xc2, 0x5a, 0xd3, 0xe6, 0x9b, 0xd5, 0xce, 0xa3, 0x6c, 0xc3, 0xfc, 0x0c, 0xdd, 0xbb, 0xd1, + 0xa9, 0x47, 0x86, 0x38, 0x8e, 0x21, 0xb4, 0xd6, 0x75, 0x2a, 0x7b, 0xc1, 0x0d, 0x3e, 0x7b, 0x99, + 0xda, 0x83, 0x95, 0xdf, 0x7f, 0xb3, 0x37, 0xf7, 0xf5, 0x37, 0x7b, 0x73, 0xf6, 0x5f, 0x0d, 0x74, + 0xa7, 0x57, 0x26, 0x1e, 0xb1, 0x14, 0x87, 0xff, 0xcb, 0x06, 0x3b, 0x42, 0x4d, 0x21, 0x59, 0x92, + 0x95, 0xf4, 0xc2, 0x3b, 0x94, 0xf4, 0x8a, 0x32, 0x53, 0x1b, 0xf6, 0x9f, 0x0c, 0xb4, 0xfd, 0xe8, + 0xc5, 0x88, 0xa6, 0x8c, 0xe0, 0xff, 0x0a, 0x1f, 0x9c, 0xa2, 0x35, 0xa8, 0xe1, 0x09, 0xab, 0xd1, + 0x69, 0xec, 0xb7, 0x0e, 0x7f, 0xec, 0x64, 0xe4, 0xe4, 0x94, 0x9c, 0x95, 0x13, 0x94, 0x53, 0xf7, + 0xee, 0x4e, 0xda, 0xda, 0xff, 0x34, 0xd0, 0xad, 0x4f, 0x43, 0xe6, 0xe3, 0xf0, 0x2c, 0xc4, 0x62, + 0xa8, 0x2e, 0x6f, 0xac, 0xb2, 0xe6, 0x90, 0x77, 0x8d, 0x8e, 0x6e, 0xe6, 0xac, 0x95, 0x99, 0xee, + 0xe3, 0x4f, 0xd0, 0x66, 0x59, 0xc7, 0xe5, 0xe1, 0xea, 0x64, 0x8e, 0xb7, 0x5e, 0x7f, 0xbf, 0xb7, + 0x51, 0xdc, 0x61, 0x4f, 0x1f, 0xf4, 0x43, 0x77, 0x83, 0x4c, 0x08, 0x02, 0xb3, 0x8d, 0x5a, 0xd4, + 0x27, 0x9e, 0x80, 0x17, 0x5e, 0x3c, 0x8a, 0xf4, 0xbd, 0x2c, 0xb8, 0x4d, 0xea, 0x93, 0x33, 0x78, + 0xf1, 0xc5, 0x28, 0x32, 0xef, 0xa3, 0xf7, 0x8a, 0x41, 0xe4, 0xa5, 0x38, 0xf4, 0x94, 0xbd, 0x87, + 0x83, 0x80, 0xeb, 0x6b, 0x5a, 0x75, 0xb7, 0x8a, 0xdd, 0x0b, 0x1c, 0x2a, 0x67, 0x47, 0x41, 0xc0, + 0xed, 0x7f, 0x2c, 0xa2, 0xa5, 0x3e, 0xe6, 0x38, 0x12, 0xe6, 0x39, 0xda, 0x90, 0x10, 0x25, 0x21, + 0x96, 0xe0, 0x65, 0x1c, 0x99, 0x67, 0xfa, 0xa1, 0xe6, 0xce, 0xfa, 0x6c, 0x71, 0x6a, 0xd3, 0x24, + 0x3d, 0x70, 0x7a, 0x5a, 0x7a, 0x26, 0xb1, 0x04, 0x77, 0xbd, 0xc0, 0xc8, 0x84, 0xe6, 0xcf, 0x91, + 0x25, 0xf9, 0x48, 0xc8, 0x8a, 0xbd, 0xaa, 0xb6, 0xcd, 0xae, 0xf2, 0xbd, 0x62, 0x3f, 0x6b, 0xf8, + 0xb2, 0x5d, 0xaf, 0x27, 0xaa, 0xc6, 0x7f, 0x42, 0x54, 0x67, 0x68, 0x4b, 0xb1, 0xfc, 0x34, 0xe6, + 0xc2, 0xec, 0x98, 0x9b, 0xca, 0x7e, 0x12, 0xf4, 0x4b, 0x64, 0xa6, 0x82, 0x4c, 0x63, 0x2e, 0xbe, + 0x43, 0x9c, 0xa9, 0x20, 0x93, 0x90, 0x01, 0xda, 0x15, 0xaa, 0xf8, 0xbc, 0x08, 0xa4, 0xa6, 0xbd, + 0x24, 0x84, 0x98, 0x8a, 0x61, 0x01, 0xbe, 0x34, 0x3b, 0xf8, 0x8e, 0x06, 0xfa, 0x5c, 0xe1, 0xb8, + 0x05, 0x4c, 0xee, 0xa5, 0x87, 0xda, 0xd7, 0x7b, 0x29, 0x2f, 0x68, 0x59, 0x5f, 0xd0, 0xff, 0x5d, + 0x03, 0x51, 0xde, 0xd2, 0x21, 0xba, 0x1d, 0xe1, 0x57, 0x9e, 0x1c, 0x72, 0x26, 0x65, 0x08, 0x81, + 0x97, 0x60, 0x72, 0x09, 0x52, 0xe8, 0x19, 0xd5, 0x70, 0xb7, 0x22, 0xfc, 0xea, 0xbc, 0xd8, 0xeb, + 0x67, 0x5b, 0xa6, 0x40, 0x1f, 0xd4, 0x28, 0xfd, 0x25, 0xe6, 0x81, 0x17, 0x40, 0xcc, 0x22, 0x8f, + 0xc3, 0x40, 0xf1, 0x1e, 0xce, 0xd8, 0x1d, 0xa0, 0x1c, 0x4b, 0x79, 0x23, 0xab, 0x57, 0x46, 0xd9, + 0xc4, 0x3d, 0x46, 0xe3, 0x7c, 0x76, 0xdb, 0x15, 0xf3, 0x2b, 0xb4, 0x87, 0x0a, 0xcc, 0xad, 0x61, + 0x3d, 0x06, 0xb0, 0x7d, 0xb4, 0xf9, 0x04, 0xc7, 0x81, 0x18, 0xe2, 0x4b, 0xf8, 0x1c, 0x24, 0x0e, + 0xb0, 0xc4, 0x13, 0x3d, 0xf3, 0x1c, 0xc0, 0x4b, 0x18, 0x0b, 0xb3, 0x9e, 0xc9, 0x28, 0xa8, 0xec, + 0x99, 0xc7, 0x00, 0x7d, 0xc6, 0x42, 0xd5, 0x33, 0xa6, 0x85, 0x96, 0x53, 0xe0, 0xa2, 0xaa, 0xe0, + 0x62, 0x69, 0xff, 0x04, 0x35, 0x35, 0x69, 0x1c, 0x91, 0x4b, 0x61, 0xee, 0xa2, 0xa6, 0x42, 0x02, + 0x21, 0x40, 0x58, 0x46, 0xa7, 0xb1, 0xdf, 0x74, 0x2b, 0x81, 0x2d, 0xd1, 0xce, 0x4d, 0xef, 0x22, + 0x61, 0x3e, 0x43, 0xcb, 0x09, 0xe8, 0xa1, 0xad, 0x0d, 0x5b, 0x87, 0x1f, 0x3b, 0x33, 0xbc, 0x3d, + 0x9d, 0x9b, 0x00, 0xdd, 0x02, 0xcd, 0xe6, 0xd5, 0x6b, 0x6c, 0x6a, 0x56, 0x08, 0xf3, 0x62, 0xda, + 0xe9, 0x2f, 0xde, 0xc9, 0xe9, 0x14, 0x5e, 0xe5, 0xf3, 0x43, 0xd4, 0x3a, 0xca, 0xd2, 0xfe, 0x25, + 0x15, 0xf2, 0xea, 0xb1, 0xac, 0xd6, 0x8f, 0xe5, 0x33, 0xb4, 0x9e, 0x8f, 0xb8, 0x73, 0xa6, 0x89, + 0xcf, 0xfc, 0x7f, 0x84, 0xf2, 0xd9, 0xa8, 0x08, 0x33, 0xbb, 0x96, 0x66, 0x2e, 0x39, 0x09, 0x26, + 0x46, 0xd5, 0xfc, 0xc4, 0xa8, 0xb2, 0x5d, 0xb4, 0x71, 0x21, 0xc8, 0xaf, 0x8a, 0xf7, 0xcf, 0xd3, + 0x44, 0x98, 0xb7, 0xd1, 0x92, 0xea, 0xd5, 0x1c, 0x68, 0xc1, 0x5d, 0x4c, 0x05, 0x39, 0x09, 0xcc, + 0xfd, 0xfa, 0x1b, 0x8b, 0x25, 0x1e, 0x0d, 0x84, 0x35, 0xdf, 0x69, 0xec, 0x2f, 0xb8, 0xeb, 0xa3, + 0xca, 0xfc, 0x24, 0x10, 0xf6, 0xaf, 0x51, 0xab, 0x06, 0x68, 0xae, 0xa3, 0xf9, 0x12, 0x6b, 0x9e, + 0x06, 0xe6, 0x03, 0xb4, 0x53, 0x01, 0x4d, 0xd2, 0x7d, 0x86, 0xd8, 0x74, 0xef, 0x94, 0x0a, 0x13, + 0x8c, 0x2f, 0xec, 0xa7, 0x68, 0xfb, 0xa4, 0x22, 0x97, 0x72, 0x98, 0x4c, 0x64, 0x68, 0x4c, 0x0e, + 0xe3, 0x5d, 0xd4, 0x2c, 0x7f, 0x48, 0xe8, 0xec, 0x17, 0xdc, 0x4a, 0x60, 0x47, 0xe8, 0xd6, 0x85, + 0x20, 0x67, 0x10, 0x07, 0x15, 0xd8, 0x0d, 0x07, 0x70, 0x3c, 0x0d, 0x34, 0xf3, 0x43, 0xb5, 0x72, + 0xf7, 0x17, 0x03, 0x59, 0x8f, 0x5e, 0x25, 0x8c, 0x4b, 0x08, 0xae, 0xf8, 0x7d, 0x4b, 0x12, 0x97, + 0x68, 0x4b, 0x85, 0x24, 0x20, 0x0e, 0xbc, 0x12, 0x2d, 0x3b, 0xad, 0xd6, 0xe1, 0xcf, 0x66, 0xaa, + 0xc1, 0x69, 0x77, 0x39, 0x2d, 0x6c, 0xa6, 0x53, 0x72, 0x61, 0xff, 0xc1, 0x40, 0xd6, 0x29, 0x8c, + 0x8f, 0x84, 0xa0, 0x83, 0x38, 0x82, 0x58, 0x2a, 0x46, 0xc3, 0x04, 0xd4, 0xa7, 0xf9, 0x3e, 0x5a, + 0x2b, 0xd9, 0xa0, 0x24, 0x81, 0x55, 0x77, 0xb5, 0x10, 0xea, 0xee, 0x7f, 0x80, 0x50, 0xc2, 0x21, + 0xf5, 0x88, 0x77, 0x09, 0xe3, 0xfc, 0xac, 0x76, 0xeb, 0x03, 0x31, 0xfb, 0x31, 0xe5, 0xf4, 0x47, + 0x7e, 0x48, 0xc9, 0x29, 0x8c, 0xdd, 0x15, 0xa5, 0xdf, 0x3b, 0x85, 0xb1, 0x7a, 0xe0, 0x24, 0xec, + 0x25, 0x70, 0x3d, 0xc5, 0x1a, 0x6e, 0xb6, 0xb0, 0xff, 0x68, 0xa0, 0x3b, 0x17, 0x38, 0xa4, 0x01, + 0x96, 0x8c, 0x17, 0x45, 0xd1, 0x1f, 0xf9, 0xca, 0xe2, 0x2d, 0xe7, 0x76, 0x25, 0xda, 0xf9, 0x6b, + 0xa2, 0xfd, 0x04, 0xad, 0x96, 0x65, 0xa8, 0xe2, 0x6d, 0xcc, 0x10, 0x6f, 0xab, 0xb0, 0x38, 0x85, + 0xb1, 0xfd, 0xbb, 0x5a, 0x6c, 0xc7, 0xe3, 0x1a, 0xc3, 0xf0, 0x7f, 0x13, 0x5b, 0xe9, 0xb6, 0x1e, + 0x1b, 0xa9, 0xdb, 0x5f, 0x49, 0xa0, 0x71, 0x35, 0x01, 0xfb, 0xcf, 0x06, 0xda, 0xae, 0x7b, 0x15, + 0xe7, 0xac, 0xcf, 0x47, 0x31, 0xbc, 0xcd, 0x7b, 0x55, 0xe4, 0xf3, 0xf5, 0x22, 0x7f, 0x86, 0xd6, + 0x27, 0x82, 0x12, 0xf9, 0x69, 0xfc, 0x74, 0xa6, 0x1a, 0xab, 0x71, 0x98, 0xbb, 0x56, 0xcf, 0x43, + 0x1c, 0x3f, 0xfb, 0xf6, 0x75, 0xdb, 0xf8, 0xee, 0x75, 0xdb, 0xf8, 0xfb, 0xeb, 0xb6, 0xf1, 0xd5, + 0x9b, 0xf6, 0xdc, 0x77, 0x6f, 0xda, 0x73, 0x7f, 0x7b, 0xd3, 0x9e, 0xfb, 0xcd, 0xc7, 0x03, 0x2a, + 0x87, 0x23, 0xdf, 0x21, 0x2c, 0xea, 0xe6, 0xbf, 0x94, 0x2b, 0x5f, 0x1f, 0x95, 0x7f, 0x3c, 0xa4, + 0xf7, 0xbb, 0xaf, 0x26, 0xff, 0x7d, 0x90, 0xe3, 0x04, 0x84, 0xbf, 0xa4, 0x7b, 0xef, 0xfe, 0xbf, + 0x02, 0x00, 0x00, 0xff, 0xff, 0x2b, 0x34, 0xfa, 0x5d, 0xae, 0x10, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -2088,6 +2145,50 @@ func (m *VscSendTimestamp) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ExportedVscSendTimestamp) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExportedVscSendTimestamp) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExportedVscSendTimestamp) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.VscSendTimestamps) > 0 { + for iNdEx := len(m.VscSendTimestamps) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.VscSendTimestamps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProvider(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintProvider(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *KeyAssignmentReplacement) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2602,6 +2703,25 @@ func (m *VscSendTimestamp) Size() (n int) { return n } +func (m *ExportedVscSendTimestamp) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovProvider(uint64(l)) + } + if len(m.VscSendTimestamps) > 0 { + for _, e := range m.VscSendTimestamps { + l = e.Size() + n += 1 + l + sovProvider(uint64(l)) + } + } + return n +} + func (m *KeyAssignmentReplacement) Size() (n int) { if m == nil { return 0 @@ -5009,6 +5129,122 @@ func (m *VscSendTimestamp) Unmarshal(dAtA []byte) error { } return nil } +func (m *ExportedVscSendTimestamp) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExportedVscSendTimestamp: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExportedVscSendTimestamp: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VscSendTimestamps", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VscSendTimestamps = append(m.VscSendTimestamps, VscSendTimestamp{}) + if err := m.VscSendTimestamps[len(m.VscSendTimestamps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProvider(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProvider + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *KeyAssignmentReplacement) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 From 7484dbceb66ceafad05d34d46c6b4e4f145cb022 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Wed, 19 Jul 2023 09:04:35 -0700 Subject: [PATCH 26/38] tests: regression tests for types sent over wire as json (#1160) * tests * linting is the most important part of programming --------- Co-authored-by: Marius Poke --- x/ccv/consumer/keeper/relay_test.go | 12 ++- x/ccv/types/ccv.go | 12 +-- x/ccv/types/ccv_test.go | 131 ++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 12 deletions(-) diff --git a/x/ccv/consumer/keeper/relay_test.go b/x/ccv/consumer/keeper/relay_test.go index 6dbebe273f..ed6771900e 100644 --- a/x/ccv/consumer/keeper/relay_test.go +++ b/x/ccv/consumer/keeper/relay_test.go @@ -234,13 +234,21 @@ func TestOnAcknowledgementPacket(t *testing.T) { // Set an established provider channel for later in test consumerKeeper.SetProviderChannel(ctx, channelIDToProvider) - packetData := types.NewSlashPacketData( + slashPacketData := types.NewSlashPacketData( abci.Validator{Address: bytes.HexBytes{}, Power: int64(1)}, uint64(1), stakingtypes.Infraction_INFRACTION_DOWNTIME, ) + // The type that'd be JSON marshaled and sent over the wire + consumerPacketData := types.NewConsumerPacketData( + types.SlashPacket, + &types.ConsumerPacketData_SlashPacketData{ + SlashPacketData: slashPacketData, + }, + ) + // AcknowledgePacket is in reference to a packet originally sent from this (consumer) module. packet := channeltypes.NewPacket( - packetData.GetBytes(), + consumerPacketData.GetBytes(), 1, types.ConsumerPortID, // Source port channelIDToDestChain, // Source channel diff --git a/x/ccv/types/ccv.go b/x/ccv/types/ccv.go index 91175eab5d..70921704f7 100644 --- a/x/ccv/types/ccv.go +++ b/x/ccv/types/ccv.go @@ -29,6 +29,8 @@ func (vsc ValidatorSetChangePacketData) ValidateBasic() error { return nil } +// GetBytes marshals the ValidatorSetChangePacketData into JSON string bytes +// to be sent over the wire with IBC. func (vsc ValidatorSetChangePacketData) GetBytes() []byte { valUpdateBytes := ModuleCdc.MustMarshalJSON(&vsc) return valUpdateBytes @@ -48,11 +50,6 @@ func (mat VSCMaturedPacketData) ValidateBasic() error { return nil } -func (mat VSCMaturedPacketData) GetBytes() []byte { - bytes := ModuleCdc.MustMarshalJSON(&mat) - return bytes -} - func NewSlashPacketData(validator abci.Validator, valUpdateId uint64, infractionType stakingtypes.Infraction) *SlashPacketData { return &SlashPacketData{ Validator: validator, @@ -90,11 +87,6 @@ func (vdt SlashPacketData) ValidateBasic() error { return nil } -func (vdt SlashPacketData) GetBytes() []byte { - valDowntimeBytes := ModuleCdc.MustMarshalJSON(&vdt) - return valDowntimeBytes -} - func (vdt SlashPacketData) ToV1() *SlashPacketDataV1 { return NewSlashPacketDataV1(vdt.Validator, vdt.ValsetUpdateId, vdt.Infraction) } diff --git a/x/ccv/types/ccv_test.go b/x/ccv/types/ccv_test.go index 596967b199..50164fcf73 100644 --- a/x/ccv/types/ccv_test.go +++ b/x/ccv/types/ccv_test.go @@ -1,15 +1,18 @@ package types_test import ( + "strings" "testing" "github.com/stretchr/testify/require" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/interchain-security/v3/testutil/crypto" "github.com/cosmos/interchain-security/v3/x/ccv/types" ) @@ -93,3 +96,131 @@ func TestMarshalPacketData(t *testing.T) { require.Nil(t, err) require.Equal(t, vpd, recovered, "unmarshaled packet data does not equal original value") } + +// TestVSCPacketDataWireBytes is a regression test that the JSON schema +// for ValidatorSetChangePacketData (sent over the wire) does not change. +func TestVSCPacketDataWireBytes(t *testing.T) { + cId1 := crypto.NewCryptoIdentityFromIntSeed(4732894) + cId2 := crypto.NewCryptoIdentityFromIntSeed(4732895) + + pd := types.NewValidatorSetChangePacketData( + []abci.ValidatorUpdate{ + { + PubKey: cId1.TMProtoCryptoPublicKey(), + Power: 30, + }, + { + PubKey: cId2.TMProtoCryptoPublicKey(), + Power: 20, + }, + }, + 73, + []string{"slash", "acks", "example"}, + ) + + jsonBz := pd.GetBytes() + str := string(jsonBz) + + // Expected string formatted for human readability + expectedStr := `{ + "validator_updates": [ + { + "pub_key": { + "ed25519": "SMxP2pXAuxQC7FmBn4dh4Kt5eYdQFWC/wN7oWobZKds=" + }, + "power": "30" + }, + { + "pub_key": { + "ed25519": "J/nGy0vCXhgVbr8S71B4ZgHi4fsMqtDxDlERZ+gG238=" + }, + "power": "20" + } + ], + "valset_update_id": "73", + "slash_acks": ["slash", "acks", "example"] + }` + + // Remove newlines, tabs, and spaces for comparison + expectedStr = strings.ReplaceAll(expectedStr, "\n", "") + expectedStr = strings.ReplaceAll(expectedStr, "\t", "") + expectedStr = strings.ReplaceAll(expectedStr, " ", "") + + require.Equal(t, expectedStr, str) +} + +// TestSlashPacketDataWireBytes is a regression test that the JSON schema +// for SlashPacketData (sent over the wire) does not change. +func TestSlashPacketDataWireBytes(t *testing.T) { + // Construct consumer packet data wrapping slash packet data + cId := crypto.NewCryptoIdentityFromIntSeed(4732894342) + slashPacketData := types.NewSlashPacketData( + abci.Validator{ + Address: cId.SDKValConsAddress(), + Power: int64(4328), + }, + uint64(894732), + stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN, + ) + + // The type that'd be JSON marshaled and sent over the wire + cpd := types.NewConsumerPacketData( + types.SlashPacket, + &types.ConsumerPacketData_SlashPacketData{ + SlashPacketData: slashPacketData, + }, + ) + + jsonBz := cpd.GetBytes() + str := string(jsonBz) + + // Expected string formatted for human readability + expectedStr := `{ + "type": "CONSUMER_PACKET_TYPE_SLASH", + "slashPacketData": { + "validator": { + "address": "BP9q4oXCgubvoujOKyxIxd+3IwM=", + "power": "4328" + }, + "valset_update_id": "894732", + "infraction": "INFRACTION_TYPE_DOUBLE_SIGN" + } + }` + + // Remove newlines, tabs, and spaces for comparison + expectedStr = strings.ReplaceAll(expectedStr, "\n", "") + expectedStr = strings.ReplaceAll(expectedStr, "\t", "") + expectedStr = strings.ReplaceAll(expectedStr, " ", "") + + require.Equal(t, expectedStr, str) +} + +// TestVSCMaturedPacketDataWireBytes is a regression test that the JSON schema +// for VSCMaturedPacketData (sent over the wire) does not change. +func TestVSCMaturedPacketDataWireBytes(t *testing.T) { + // Construct consumer packet data wrapping vsc matured packet data + cpd := types.ConsumerPacketData{ + Type: types.VscMaturedPacket, + Data: &types.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: types.NewVSCMaturedPacketData(84923), + }, + } + + jsonBz := cpd.GetBytes() + str := string(jsonBz) + + // Expected string formatted for human readability + expectedStr := `{ + "type": "CONSUMER_PACKET_TYPE_VSCM", + "vscMaturedPacketData": { + "valset_update_id": "84923" + } + }` + + // Remove newlines, tabs, and spaces for comparison + expectedStr = strings.ReplaceAll(expectedStr, "\n", "") + expectedStr = strings.ReplaceAll(expectedStr, "\t", "") + expectedStr = strings.ReplaceAll(expectedStr, " ", "") + + require.Equal(t, expectedStr, str) +} From 5e5e2fa7508b69e7242ec007347cabe8ea676f79 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Tue, 1 Aug 2023 13:29:57 +0200 Subject: [PATCH 27/38] test: Fix mismatching code/comment and use WaitTime instead of sleep (#1174) Fix mismatching comment and use WaitTime instead of sleep --- tests/e2e/actions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 3dd8fb704e..8faca43c0a 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1655,8 +1655,8 @@ type unjailValidatorAction struct { // Sends an unjail transaction to the provider chain func (tr TestRun) unjailValidator(action unjailValidatorAction, verbose bool) { - // wait a block to be sure downtime_jail_duration has elapsed - time.Sleep(61 * time.Second) + // wait until downtime_jail_duration has elapsed, to make sure the validator can be unjailed + tr.WaitTime(61 * time.Second) //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. cmd := exec.Command("docker", "exec", From b72ad0d62832043675b8148727def24925b0eb9a Mon Sep 17 00:00:00 2001 From: karolos Date: Wed, 2 Aug 2023 15:02:42 +0200 Subject: [PATCH 28/38] docs: remove an obsolete comment before setting the order of EndBlockers (#1081) Remove the whole comment because the described order does not constitute an interchain security requirement. --- app/provider/app.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/provider/app.go b/app/provider/app.go index a25e3f4ab6..c054f4a00b 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -530,12 +530,7 @@ func New( vestingtypes.ModuleName, providertypes.ModuleName, ) - // Interchain Security requirements - // - provider.EndBlock gets validator updates from the staking module; - // thus, staking.EndBlock must be executed before provider.EndBlock; - // - creating a new consumer chain requires the following order, - // CreateChildClient(), staking.EndBlock, provider.EndBlock; - // thus, gov.EndBlock must be executed before staking.EndBlock + app.MM.SetOrderEndBlockers( crisistypes.ModuleName, govtypes.ModuleName, From c49651332ff824235e75fb3ae32df43858c34fb4 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:52:57 +0200 Subject: [PATCH 29/38] chore: Migrate containers to informalsystems repos (#1172) * Migrate containers to informalsystems repos and fix versions * Remove fixed shas from container images --- Dockerfile | 4 ++-- tests/e2e/testnet-scripts/start-chain.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index dee88dae7c..7c45187034 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,10 +32,10 @@ RUN make install FROM ghcr.io/informalsystems/hermes:1.4.1 AS hermes-builder # Get CometMock -FROM informalofftermatt/cometmock:latest as cometmock-builder +FROM ghcr.io/informalsystems/cometmock:v0.37.x as cometmock-builder # Get GoRelayer -FROM informalofftermatt/gorelayer:nogas AS gorelayer-builder +FROM ghcr.io/informalsystems/relayer-no-gas-sim:v2.3.0-rc4-no-gas-sim AS gorelayer-builder FROM --platform=linux/amd64 fedora:36 RUN dnf update -y diff --git a/tests/e2e/testnet-scripts/start-chain.sh b/tests/e2e/testnet-scripts/start-chain.sh index a3609acf73..88c8455e51 100644 --- a/tests/e2e/testnet-scripts/start-chain.sh +++ b/tests/e2e/testnet-scripts/start-chain.sh @@ -362,7 +362,7 @@ NODE_HOMES=${NODE_HOMES%?} # CometMock takes the role of the query node if [[ "$USE_COMETMOCK" == "true" ]]; then sleep 2 - ip netns exec $QUERY_NET_NAMESPACE_NAME cometmock $NODE_LISTEN_ADDR_STR /$CHAIN_ID/genesis.json tcp://$CHAIN_IP_PREFIX.$QUERY_IP_SUFFIX:26658 $NODE_HOMES &> cometmock_${CHAIN_ID}_out.log & + ip netns exec $QUERY_NET_NAMESPACE_NAME cometmock $NODE_LISTEN_ADDR_STR /$CHAIN_ID/genesis.json tcp://$CHAIN_IP_PREFIX.$QUERY_IP_SUFFIX:26658 $NODE_HOMES grpc &> cometmock_${CHAIN_ID}_out.log & sleep 3 fi From c0d8314490bf403363968c131a5e9e6f399390a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:56:56 +0200 Subject: [PATCH 30/38] build(deps): bump github.com/tidwall/gjson from 1.14.4 to 1.15.0 (#1171) Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.14.4 to 1.15.0. - [Commits](https://github.com/tidwall/gjson/compare/v1.14.4...v1.15.0) --- updated-dependencies: - dependency-name: github.com/tidwall/gjson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7947a36e66..7ee8399acc 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/spf13/cast v1.5.1 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.4 - github.com/tidwall/gjson v1.14.4 + github.com/tidwall/gjson v1.15.0 golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc golang.org/x/net v0.12.0 // indirect diff --git a/go.sum b/go.sum index 26ceadb4ea..5377f849fa 100644 --- a/go.sum +++ b/go.sum @@ -1115,8 +1115,8 @@ github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= -github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.15.0 h1:5n/pM+v3r5ujuNl4YLZLsQ+UE5jlkLVm7jMzT5Mpolw= +github.com/tidwall/gjson v1.15.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= From 8c40f32869936bfe9b4a1dee9a44b3a6b8f8f484 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 08:45:34 -0700 Subject: [PATCH 31/38] build(deps): bump google.golang.org/grpc from 1.56.2 to 1.57.0 (#1170) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.56.2 to 1.57.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.56.2...v1.57.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 11 ++++++++--- go.sum | 14 ++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 7ee8399acc..ff38553879 100644 --- a/go.mod +++ b/go.mod @@ -26,8 +26,8 @@ require ( golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc golang.org/x/net v0.12.0 // indirect golang.org/x/sys v0.10.0 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 - google.golang.org/grpc v1.56.2 + google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 // indirect + google.golang.org/grpc v1.57.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -165,13 +165,18 @@ require ( sigs.k8s.io/yaml v1.3.0 // indirect ) -require github.com/spf13/viper v1.15.0 +require ( + github.com/spf13/viper v1.15.0 + google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e +) require ( cosmossdk.io/log v1.1.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/rs/zerolog v1.29.1 // indirect + golang.org/x/sync v0.1.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect ) // following versions might cause unexpected behavior diff --git a/go.sum b/go.sum index 5377f849fa..4a93323e50 100644 --- a/go.sum +++ b/go.sum @@ -1362,6 +1362,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1747,8 +1749,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 h1:9JucMWR7sPvCxUFd6UsOUNmA5kCcWOfORaT3tpAsKQs= +google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e h1:AZX1ra8YbFMSb7+1pI8S9v4rrgRR7jU1FmuFSSjTVcQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1790,8 +1796,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= -google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= From 006680a60bfe7fc48ee142bb0107a3e82e076470 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 18:05:04 +0200 Subject: [PATCH 32/38] build(deps): bump bufbuild/buf-setup-action from 1.25.0 to 1.25.1 (#1187) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.25.0 to 1.25.1. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.25.0...v1.25.1) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 9764edd806..a3291cec36 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.25.0 + - uses: bufbuild/buf-setup-action@v1.25.1 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 8f80957d92..deb110dad0 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.25.0 + - uses: bufbuild/buf-setup-action@v1.25.1 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From aaa545dc936cb3b56adebdef327dbedc45066315 Mon Sep 17 00:00:00 2001 From: yaruwangway <69694322+yaruwangway@users.noreply.github.com> Date: Wed, 9 Aug 2023 10:45:26 +0200 Subject: [PATCH 33/38] feat: add provider info query (#1164) * feat: add provider info query * feat: refactor query providerInfo * feat: query provider info * feat: query all provider info * feat: add provider info query cli * feat: add consumer info to provider info query * feat: add consumer chain-id to provider info query * fix: return err when client, channel not found * fix: lint * chore: fix lint * chore: lint fix * chore: add nonlint * chore: add nonlint to the right place * chore: add nolint nolintlint * chore: nolint all * chore: nolint:golint * chore: fix package import order * chore: update queryProviderChainInfo to queryProviderInfo * chore: update proto * update query.go * docs: update changelog --- CHANGELOG.md | 1 + .../ccv/consumer/v1/query.proto | 18 + testutil/keeper/mocks.go | 10 + x/ccv/consumer/client/cli/query.go | 32 +- x/ccv/consumer/keeper/grpc_query.go | 17 +- x/ccv/consumer/keeper/provider_info.go | 54 ++ x/ccv/consumer/types/query.pb.go | 780 +++++++++++++++++- x/ccv/consumer/types/query.pb.gw.go | 65 ++ x/ccv/types/expected_keepers.go | 1 + 9 files changed, 940 insertions(+), 38 deletions(-) create mode 100644 x/ccv/consumer/keeper/provider_info.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 818bdca374..5003260943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Add an entry to the unreleased section whenever merging a PR to main that is not * (fix!) revert consumer packet data changes from #1037 [#1150](https://github.com/cosmos/interchain-security/pull/1150) * (fix!) proper deletion of pending packets [#1146](https://github.com/cosmos/interchain-security/pull/1146) * (feat!) optimize pending packets storage on consumer, with migration! [#1037](https://github.com/cosmos/interchain-security/pull/1037) +* (feat) introduce the gRPC query `/interchain_security/ccv/consumer/provider-info` and CLI command `interchain-security-cd q ccvconsumer provider-info`to retrieve provider info from the consumer chain. ## v3.1.0 diff --git a/proto/interchain_security/ccv/consumer/v1/query.proto b/proto/interchain_security/ccv/consumer/v1/query.proto index df90b1d0aa..43a7b0bccc 100644 --- a/proto/interchain_security/ccv/consumer/v1/query.proto +++ b/proto/interchain_security/ccv/consumer/v1/query.proto @@ -19,6 +19,10 @@ service Query { rpc QueryParams(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/interchain_security/ccv/consumer/params"; } + + rpc QueryProviderInfo(QueryProviderInfoRequest) returns (QueryProviderInfoResponse) { + option (google.api.http).get = "/interchain_security/ccv/consumer/provider-info"; + } } // NextFeeDistributionEstimate holds information about next fee distribution @@ -52,3 +56,17 @@ message QueryParamsResponse { // params holds all the parameters of this module. Params params = 1 [ (gogoproto.nullable) = false ]; } + +message QueryProviderInfoRequest {} + +message QueryProviderInfoResponse { + ChainInfo consumer = 1 [ (gogoproto.nullable) = false ]; + ChainInfo provider = 2 [ (gogoproto.nullable) = false ]; +} + +message ChainInfo { + string chainID = 1; + string clientID = 2; + string connectionID = 3; + string channelID = 4; +} diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index fea83079d3..b075819cc4 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -564,6 +564,16 @@ func (m *MockChannelKeeper) GetChannel(ctx types0.Context, srcPort, srcChan stri return ret0, ret1 } +func (m *MockChannelKeeper) GetChannelConnection(ctx types0.Context, portID, channelID string) (string, exported.ConnectionI, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetChannelConnection", ctx, portID, channelID) + ret0, _ := ret[0].(string) + ret1, _ := ret[0].(exported.ConnectionI) + ret2, _ := ret[1].(error) + + return ret0, ret1, ret2 +} + // GetChannel indicates an expected call of GetChannel. func (mr *MockChannelKeeperMockRecorder) GetChannel(ctx, srcPort, srcChan interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() diff --git a/x/ccv/consumer/client/cli/query.go b/x/ccv/consumer/client/cli/query.go index a961b9642c..dc1ecfc2f4 100644 --- a/x/ccv/consumer/client/cli/query.go +++ b/x/ccv/consumer/client/cli/query.go @@ -19,7 +19,10 @@ func NewQueryCmd() *cobra.Command { RunE: client.ValidateCmd, } - cmd.AddCommand(CmdNextFeeDistribution()) + cmd.AddCommand( + CmdNextFeeDistribution(), + CmdProviderInfo(), + ) return cmd } @@ -50,3 +53,30 @@ func CmdNextFeeDistribution() *cobra.Command { return cmd } + +func CmdProviderInfo() *cobra.Command { + cmd := &cobra.Command{ + Use: "provider-info", + Short: "Query provider info", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryProviderInfoRequest{} + res, err := queryClient.QueryProviderInfo(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ccv/consumer/keeper/grpc_query.go b/x/ccv/consumer/keeper/grpc_query.go index 174f591497..80e6b695a9 100644 --- a/x/ccv/consumer/keeper/grpc_query.go +++ b/x/ccv/consumer/keeper/grpc_query.go @@ -11,9 +11,9 @@ import ( "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" ) -var _ types.QueryServer = Keeper{} +var _ types.QueryServer = Keeper{} //nolint:golint -func (k Keeper) QueryNextFeeDistribution(c context.Context, +func (k Keeper) QueryNextFeeDistribution(c context.Context, //nolint:golint req *types.QueryNextFeeDistributionEstimateRequest, ) (*types.QueryNextFeeDistributionEstimateResponse, error) { ctx := sdk.UnwrapSDKContext(c) @@ -27,7 +27,7 @@ func (k Keeper) QueryNextFeeDistribution(c context.Context, return &types.QueryNextFeeDistributionEstimateResponse{Data: &nextDist}, nil } -func (k Keeper) QueryParams(c context.Context, +func (k Keeper) QueryParams(c context.Context, //nolint:golint req *types.QueryParamsRequest, ) (*types.QueryParamsResponse, error) { ctx := sdk.UnwrapSDKContext(c) @@ -40,3 +40,14 @@ func (k Keeper) QueryParams(c context.Context, return &types.QueryParamsResponse{Params: p}, nil } + +func (k Keeper) QueryProviderInfo(c context.Context, //nolint:golint + req *types.QueryProviderInfoRequest, +) (*types.QueryProviderInfoResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + return k.GetProviderInfo(ctx) +} diff --git a/x/ccv/consumer/keeper/provider_info.go b/x/ccv/consumer/keeper/provider_info.go new file mode 100644 index 0000000000..d8dfef100a --- /dev/null +++ b/x/ccv/consumer/keeper/provider_info.go @@ -0,0 +1,54 @@ +package keeper + +import ( + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" //nolint:golint + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" +) + +func (k Keeper) GetProviderInfo(ctx sdk.Context) (*types.QueryProviderInfoResponse, error) { //nolint:golint + consumerChannelID, found := k.GetProviderChannel(ctx) + if !found { + return nil, ccvtypes.ErrChannelNotFound + } + consumerChannel, found := k.channelKeeper.GetChannel(ctx, ccvtypes.ConsumerPortID, consumerChannelID) + if !found { + return nil, ccvtypes.ErrChannelNotFound + } + + // from channel get connection + consumerConnectionID, consumerConnection, err := k.channelKeeper.GetChannelConnection(ctx, ccvtypes.ConsumerPortID, consumerChannelID) + if err != nil { + return nil, err + } + + providerChannelID := consumerChannel.GetCounterparty().GetChannelID() + providerConnection := consumerConnection.GetCounterparty() + + consumerClientState, found := k.clientKeeper.GetClientState(ctx, consumerConnection.GetClientID()) + if !found { + return nil, ccvtypes.ErrClientNotFound + } + providerChainID := consumerClientState.(*ibctm.ClientState).ChainId + + resp := types.QueryProviderInfoResponse{ + Consumer: types.ChainInfo{ + ChainID: ctx.ChainID(), + ClientID: consumerConnection.GetClientID(), + ConnectionID: consumerConnectionID, + ChannelID: consumerChannelID, + }, + + Provider: types.ChainInfo{ + ChainID: providerChainID, + ClientID: providerConnection.GetClientID(), + ConnectionID: providerConnection.GetConnectionID(), + ChannelID: providerChannelID, + }, + } + + return &resp, nil +} diff --git a/x/ccv/consumer/types/query.pb.go b/x/ccv/consumer/types/query.pb.go index 5b9c52d962..effe4757ef 100644 --- a/x/ccv/consumer/types/query.pb.go +++ b/x/ccv/consumer/types/query.pb.go @@ -295,12 +295,171 @@ func (m *QueryParamsResponse) GetParams() Params { return Params{} } +type QueryProviderInfoRequest struct { +} + +func (m *QueryProviderInfoRequest) Reset() { *m = QueryProviderInfoRequest{} } +func (m *QueryProviderInfoRequest) String() string { return proto.CompactTextString(m) } +func (*QueryProviderInfoRequest) ProtoMessage() {} +func (*QueryProviderInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f627751d3cc10225, []int{5} +} +func (m *QueryProviderInfoRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProviderInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProviderInfoRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProviderInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProviderInfoRequest.Merge(m, src) +} +func (m *QueryProviderInfoRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryProviderInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProviderInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProviderInfoRequest proto.InternalMessageInfo + +type QueryProviderInfoResponse struct { + Consumer ChainInfo `protobuf:"bytes,1,opt,name=consumer,proto3" json:"consumer"` + Provider ChainInfo `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider"` +} + +func (m *QueryProviderInfoResponse) Reset() { *m = QueryProviderInfoResponse{} } +func (m *QueryProviderInfoResponse) String() string { return proto.CompactTextString(m) } +func (*QueryProviderInfoResponse) ProtoMessage() {} +func (*QueryProviderInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f627751d3cc10225, []int{6} +} +func (m *QueryProviderInfoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProviderInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProviderInfoResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProviderInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProviderInfoResponse.Merge(m, src) +} +func (m *QueryProviderInfoResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryProviderInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProviderInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProviderInfoResponse proto.InternalMessageInfo + +func (m *QueryProviderInfoResponse) GetConsumer() ChainInfo { + if m != nil { + return m.Consumer + } + return ChainInfo{} +} + +func (m *QueryProviderInfoResponse) GetProvider() ChainInfo { + if m != nil { + return m.Provider + } + return ChainInfo{} +} + +type ChainInfo struct { + ChainID string `protobuf:"bytes,1,opt,name=chainID,proto3" json:"chainID,omitempty"` + ClientID string `protobuf:"bytes,2,opt,name=clientID,proto3" json:"clientID,omitempty"` + ConnectionID string `protobuf:"bytes,3,opt,name=connectionID,proto3" json:"connectionID,omitempty"` + ChannelID string `protobuf:"bytes,4,opt,name=channelID,proto3" json:"channelID,omitempty"` +} + +func (m *ChainInfo) Reset() { *m = ChainInfo{} } +func (m *ChainInfo) String() string { return proto.CompactTextString(m) } +func (*ChainInfo) ProtoMessage() {} +func (*ChainInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_f627751d3cc10225, []int{7} +} +func (m *ChainInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ChainInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ChainInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ChainInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChainInfo.Merge(m, src) +} +func (m *ChainInfo) XXX_Size() int { + return m.Size() +} +func (m *ChainInfo) XXX_DiscardUnknown() { + xxx_messageInfo_ChainInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_ChainInfo proto.InternalMessageInfo + +func (m *ChainInfo) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +func (m *ChainInfo) GetClientID() string { + if m != nil { + return m.ClientID + } + return "" +} + +func (m *ChainInfo) GetConnectionID() string { + if m != nil { + return m.ConnectionID + } + return "" +} + +func (m *ChainInfo) GetChannelID() string { + if m != nil { + return m.ChannelID + } + return "" +} + func init() { proto.RegisterType((*NextFeeDistributionEstimate)(nil), "interchain_security.ccv.consumer.v1.NextFeeDistributionEstimate") proto.RegisterType((*QueryNextFeeDistributionEstimateRequest)(nil), "interchain_security.ccv.consumer.v1.QueryNextFeeDistributionEstimateRequest") proto.RegisterType((*QueryNextFeeDistributionEstimateResponse)(nil), "interchain_security.ccv.consumer.v1.QueryNextFeeDistributionEstimateResponse") proto.RegisterType((*QueryParamsRequest)(nil), "interchain_security.ccv.consumer.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "interchain_security.ccv.consumer.v1.QueryParamsResponse") + proto.RegisterType((*QueryProviderInfoRequest)(nil), "interchain_security.ccv.consumer.v1.QueryProviderInfoRequest") + proto.RegisterType((*QueryProviderInfoResponse)(nil), "interchain_security.ccv.consumer.v1.QueryProviderInfoResponse") + proto.RegisterType((*ChainInfo)(nil), "interchain_security.ccv.consumer.v1.ChainInfo") } func init() { @@ -308,40 +467,49 @@ func init() { } var fileDescriptor_f627751d3cc10225 = []byte{ - // 521 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcf, 0x8b, 0xd3, 0x40, - 0x14, 0x6e, 0xfa, 0x4b, 0x9c, 0xc5, 0xcb, 0x58, 0x21, 0x54, 0x89, 0x4b, 0x15, 0xac, 0x4a, 0x33, - 0x6e, 0x7b, 0x58, 0x3d, 0x88, 0xb2, 0xea, 0xa2, 0xa0, 0xb2, 0x16, 0x41, 0xf0, 0xb2, 0x4e, 0xa7, - 0x6f, 0xd3, 0x81, 0x26, 0x93, 0x9d, 0x99, 0x84, 0xf6, 0x26, 0xfe, 0x01, 0x22, 0xf8, 0x9f, 0x78, - 0xf1, 0x5f, 0xd8, 0xe3, 0x82, 0x17, 0x4f, 0x22, 0xad, 0x7f, 0x84, 0x47, 0xc9, 0x24, 0x59, 0x53, - 0xd0, 0x6d, 0x04, 0x6f, 0xd3, 0xef, 0x7b, 0xef, 0x7b, 0xdf, 0x7c, 0xf3, 0x1a, 0x44, 0x78, 0xa0, - 0x41, 0xb2, 0x09, 0xe5, 0xc1, 0xbe, 0x02, 0x16, 0x49, 0xae, 0xe7, 0x84, 0xb1, 0x98, 0x30, 0x11, - 0xa8, 0xc8, 0x07, 0x49, 0xe2, 0x2d, 0x72, 0x18, 0x81, 0x9c, 0xbb, 0xa1, 0x14, 0x5a, 0xe0, 0x2b, - 0x7f, 0x68, 0x70, 0x19, 0x8b, 0xdd, 0xbc, 0xc1, 0x8d, 0xb7, 0xda, 0x2d, 0x4f, 0x78, 0xc2, 0xd4, - 0x93, 0xe4, 0x94, 0xb6, 0xb6, 0x2f, 0x79, 0x42, 0x78, 0x53, 0x20, 0x34, 0xe4, 0x84, 0x06, 0x81, - 0xd0, 0x54, 0x73, 0x11, 0xa8, 0x8c, 0xed, 0x97, 0x71, 0x72, 0x32, 0xc4, 0xf4, 0x74, 0xde, 0x57, - 0xd1, 0xc5, 0xe7, 0x30, 0xd3, 0xbb, 0x00, 0x0f, 0xb9, 0xd2, 0x92, 0x8f, 0xa2, 0x44, 0xf2, 0x91, - 0xd2, 0xdc, 0xa7, 0x1a, 0xf0, 0x55, 0x74, 0x8e, 0x45, 0x52, 0x42, 0xa0, 0x1f, 0x03, 0xf7, 0x26, - 0xda, 0xb6, 0x36, 0xad, 0x6e, 0x6d, 0xb8, 0x0a, 0x62, 0x07, 0xa1, 0x29, 0x55, 0x79, 0x49, 0xd5, - 0x94, 0x14, 0x90, 0x84, 0x0f, 0x60, 0x96, 0xf3, 0xb5, 0x94, 0xff, 0x8d, 0xe0, 0x01, 0xba, 0x30, - 0x2e, 0x4c, 0xdf, 0x3f, 0x90, 0x94, 0x25, 0x07, 0xbb, 0xbe, 0x69, 0x75, 0xcf, 0x0e, 0x5b, 0x45, - 0x72, 0x37, 0xe3, 0x70, 0x0b, 0x35, 0xb4, 0xd0, 0x74, 0x6a, 0x37, 0x4c, 0x51, 0xfa, 0x23, 0x19, - 0xa5, 0xc5, 0x9e, 0x14, 0x31, 0x1f, 0x83, 0xb4, 0x9b, 0x86, 0x2a, 0x20, 0x29, 0xff, 0x20, 0x0b, - 0xc1, 0x3e, 0x93, 0xf3, 0x39, 0xd2, 0xb9, 0x8e, 0xae, 0xbd, 0x48, 0x1e, 0xeb, 0x94, 0x50, 0x86, - 0x70, 0x18, 0x81, 0xd2, 0x9d, 0xb7, 0x16, 0xea, 0xae, 0xaf, 0x55, 0xa1, 0x08, 0x14, 0xe0, 0x97, - 0xa8, 0x3e, 0xa6, 0x9a, 0x9a, 0xfc, 0x36, 0xfa, 0xf7, 0xdd, 0x12, 0x4b, 0xe0, 0x9e, 0xa6, 0x6b, - 0xd4, 0x3a, 0x2d, 0x84, 0x8d, 0x83, 0x3d, 0x2a, 0xa9, 0xaf, 0x72, 0x63, 0x6f, 0xd0, 0xf9, 0x15, - 0x34, 0xb3, 0xf0, 0x04, 0x35, 0x43, 0x83, 0x64, 0x26, 0x6e, 0x96, 0x32, 0x91, 0x8a, 0xec, 0xd4, - 0x8f, 0xbe, 0x5d, 0xae, 0x0c, 0x33, 0x81, 0xfe, 0xe7, 0x1a, 0x6a, 0x98, 0x11, 0xf8, 0xa7, 0x85, - 0xec, 0xbf, 0x85, 0x80, 0x9f, 0x96, 0x9a, 0x50, 0x32, 0xef, 0xf6, 0xb3, 0xff, 0xa4, 0x96, 0xc6, - 0xd1, 0xb9, 0xf7, 0xee, 0xcb, 0x8f, 0x8f, 0xd5, 0x3b, 0x78, 0x7b, 0xfd, 0x3f, 0x38, 0x59, 0xd5, - 0xde, 0x01, 0x40, 0xaf, 0xb8, 0x88, 0xf8, 0x93, 0x85, 0x36, 0x0a, 0x39, 0xe3, 0xed, 0xf2, 0xfe, - 0x56, 0xde, 0xab, 0x7d, 0xfb, 0xdf, 0x1b, 0xb3, 0x3b, 0xdc, 0x32, 0x77, 0xb8, 0x81, 0xbb, 0xeb, - 0xef, 0x90, 0xbe, 0xdc, 0xce, 0xab, 0xa3, 0x85, 0x63, 0x1d, 0x2f, 0x1c, 0xeb, 0xfb, 0xc2, 0xb1, - 0x3e, 0x2c, 0x9d, 0xca, 0xf1, 0xd2, 0xa9, 0x7c, 0x5d, 0x3a, 0x95, 0xd7, 0x77, 0x3d, 0xae, 0x27, - 0xd1, 0xc8, 0x65, 0xc2, 0x27, 0x4c, 0x28, 0x5f, 0xa8, 0x82, 0x68, 0xef, 0x44, 0x34, 0x1e, 0x90, - 0xd9, 0xaa, 0xb2, 0x9e, 0x87, 0xa0, 0x46, 0x4d, 0xf3, 0x41, 0x19, 0xfc, 0x0a, 0x00, 0x00, 0xff, - 0xff, 0xcc, 0x57, 0x2e, 0xd6, 0x10, 0x05, 0x00, 0x00, + // 672 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x41, 0x6b, 0x13, 0x4f, + 0x14, 0xcf, 0xa6, 0x4d, 0xdb, 0xbc, 0xfe, 0xff, 0x07, 0xc7, 0x08, 0xeb, 0x5a, 0xd6, 0xb2, 0x0a, + 0x46, 0x25, 0xbb, 0xb6, 0x3d, 0x54, 0x0f, 0x55, 0xb1, 0xb1, 0x18, 0x50, 0xa9, 0x8b, 0x20, 0x78, + 0xa9, 0xd3, 0xe9, 0x34, 0x19, 0x48, 0x66, 0xd2, 0x9d, 0xd9, 0xd0, 0xde, 0x44, 0xf1, 0x2a, 0x82, + 0xdf, 0xc4, 0x2f, 0xe0, 0xb5, 0xe0, 0xa5, 0xe0, 0xc5, 0x93, 0x48, 0xeb, 0x87, 0xf0, 0x28, 0x3b, + 0x3b, 0x9b, 0x6e, 0x68, 0x69, 0xb7, 0xea, 0x6d, 0xe7, 0xfd, 0xde, 0xfb, 0xbd, 0xdf, 0x7b, 0xf3, + 0xde, 0x2c, 0x04, 0x8c, 0x2b, 0x1a, 0x91, 0x0e, 0x66, 0x7c, 0x4d, 0x52, 0x12, 0x47, 0x4c, 0xed, + 0x04, 0x84, 0x0c, 0x02, 0x22, 0xb8, 0x8c, 0x7b, 0x34, 0x0a, 0x06, 0x73, 0xc1, 0x56, 0x4c, 0xa3, + 0x1d, 0xbf, 0x1f, 0x09, 0x25, 0xd0, 0x95, 0x63, 0x02, 0x7c, 0x42, 0x06, 0x7e, 0x16, 0xe0, 0x0f, + 0xe6, 0x9c, 0x5a, 0x5b, 0xb4, 0x85, 0xf6, 0x0f, 0x92, 0xaf, 0x34, 0xd4, 0x99, 0x69, 0x0b, 0xd1, + 0xee, 0xd2, 0x00, 0xf7, 0x59, 0x80, 0x39, 0x17, 0x0a, 0x2b, 0x26, 0xb8, 0x34, 0xe8, 0x7c, 0x11, + 0x25, 0xc3, 0x24, 0x3a, 0xc6, 0x7b, 0x5f, 0x86, 0x4b, 0x4f, 0xe9, 0xb6, 0x5a, 0xa1, 0xb4, 0xc9, + 0xa4, 0x8a, 0xd8, 0x7a, 0x9c, 0x50, 0x3e, 0x94, 0x8a, 0xf5, 0xb0, 0xa2, 0xe8, 0x2a, 0xfc, 0x4f, + 0xe2, 0x28, 0xa2, 0x5c, 0x3d, 0xa2, 0xac, 0xdd, 0x51, 0xb6, 0x35, 0x6b, 0xd5, 0xc7, 0xc2, 0x51, + 0x23, 0x72, 0x01, 0xba, 0x58, 0x66, 0x2e, 0x65, 0xed, 0x92, 0xb3, 0x24, 0x38, 0xa7, 0xdb, 0x19, + 0x3e, 0x96, 0xe2, 0x87, 0x16, 0xb4, 0x00, 0x17, 0x36, 0x72, 0xd9, 0xd7, 0x36, 0x23, 0x4c, 0x92, + 0x0f, 0x7b, 0x7c, 0xd6, 0xaa, 0x57, 0xc3, 0x5a, 0x1e, 0x5c, 0x31, 0x18, 0xaa, 0x41, 0x45, 0x09, + 0x85, 0xbb, 0x76, 0x45, 0x3b, 0xa5, 0x87, 0x24, 0x95, 0x12, 0xab, 0x91, 0x18, 0xb0, 0x0d, 0x1a, + 0xd9, 0x13, 0x1a, 0xca, 0x59, 0x52, 0x7c, 0xd9, 0x34, 0xc1, 0x9e, 0xcc, 0xf0, 0xcc, 0xe2, 0x5d, + 0x87, 0x6b, 0xcf, 0x92, 0xcb, 0x3a, 0xa1, 0x29, 0x21, 0xdd, 0x8a, 0xa9, 0x54, 0xde, 0x6b, 0x0b, + 0xea, 0xa7, 0xfb, 0xca, 0xbe, 0xe0, 0x92, 0xa2, 0xe7, 0x30, 0xbe, 0x81, 0x15, 0xd6, 0xfd, 0x9b, + 0x9e, 0xbf, 0xef, 0x17, 0x18, 0x02, 0xff, 0x24, 0x5e, 0xcd, 0xe6, 0xd5, 0x00, 0x69, 0x05, 0xab, + 0x38, 0xc2, 0x3d, 0x99, 0x09, 0x7b, 0x05, 0xe7, 0x47, 0xac, 0x46, 0x42, 0x0b, 0x26, 0xfa, 0xda, + 0x62, 0x44, 0xdc, 0x2c, 0x24, 0x22, 0x25, 0x79, 0x30, 0xbe, 0xfb, 0xfd, 0x72, 0x29, 0x34, 0x04, + 0x9e, 0x03, 0x76, 0x9a, 0xc1, 0xb4, 0xb5, 0xc5, 0x37, 0x45, 0x96, 0xfd, 0xb3, 0x05, 0x17, 0x8f, + 0x01, 0x8d, 0x88, 0x55, 0x98, 0xca, 0xd8, 0x8d, 0x0c, 0xbf, 0x90, 0x8c, 0xe5, 0x04, 0x4e, 0x98, + 0x8c, 0x92, 0x21, 0x4b, 0xc2, 0xd8, 0xcf, 0xee, 0xbb, 0xfc, 0x37, 0x8c, 0x19, 0x8b, 0xf7, 0xd6, + 0x82, 0xea, 0x10, 0x45, 0x36, 0x4c, 0x6a, 0xa6, 0x56, 0x53, 0x0b, 0xae, 0x86, 0xd9, 0x11, 0x39, + 0x30, 0x45, 0xba, 0x8c, 0x72, 0xd5, 0x6a, 0xea, 0xcc, 0xd5, 0x70, 0x78, 0x46, 0x1e, 0xfc, 0x47, + 0x04, 0xe7, 0x54, 0xcf, 0x6a, 0xab, 0xa9, 0x87, 0xbe, 0x1a, 0x8e, 0xd8, 0xd0, 0x0c, 0x54, 0x49, + 0x07, 0x73, 0x4e, 0xbb, 0xad, 0xa6, 0x19, 0xf5, 0x43, 0xc3, 0xfc, 0xbb, 0x0a, 0x54, 0x74, 0x1f, + 0xd1, 0x2f, 0xcb, 0xb4, 0xfb, 0x98, 0x81, 0x40, 0x8f, 0x0b, 0x15, 0x5b, 0x70, 0xa6, 0x9d, 0x27, + 0xff, 0x88, 0x2d, 0xbd, 0x6d, 0xef, 0xde, 0x9b, 0xaf, 0x3f, 0x3f, 0x96, 0xef, 0xa0, 0xc5, 0xd3, + 0x5f, 0xc9, 0xe4, 0x39, 0x68, 0x6c, 0x52, 0xda, 0xc8, 0x2f, 0x3b, 0xfa, 0x64, 0xc1, 0x74, 0x6e, + 0x96, 0xd1, 0x62, 0x71, 0x7d, 0x23, 0x3b, 0xe1, 0xdc, 0x3e, 0x7b, 0xa0, 0xa9, 0xe1, 0x96, 0xae, + 0xe1, 0x06, 0xaa, 0x9f, 0x5e, 0x43, 0xba, 0x1d, 0xe8, 0x8b, 0x05, 0xe7, 0x8e, 0x6c, 0x00, 0x5a, + 0x3a, 0x83, 0x82, 0xa3, 0x6b, 0xe5, 0xdc, 0xfd, 0xd3, 0x70, 0x53, 0xc6, 0xa2, 0x2e, 0x63, 0x0e, + 0x05, 0x05, 0xca, 0x30, 0xf1, 0x0d, 0x96, 0x6c, 0xc7, 0x8b, 0xdd, 0x7d, 0xd7, 0xda, 0xdb, 0x77, + 0xad, 0x1f, 0xfb, 0xae, 0xf5, 0xe1, 0xc0, 0x2d, 0xed, 0x1d, 0xb8, 0xa5, 0x6f, 0x07, 0x6e, 0xe9, + 0xe5, 0x52, 0x9b, 0xa9, 0x4e, 0xbc, 0xee, 0x13, 0xd1, 0x0b, 0x88, 0x90, 0x3d, 0x21, 0x73, 0xdc, + 0x8d, 0x21, 0xf7, 0x60, 0x21, 0xd8, 0x1e, 0x4d, 0xa0, 0x76, 0xfa, 0x54, 0xae, 0x4f, 0xe8, 0x5f, + 0xd0, 0xc2, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x78, 0x73, 0x9f, 0x0e, 0x42, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -361,6 +529,7 @@ type QueryClient interface { QueryNextFeeDistribution(ctx context.Context, in *QueryNextFeeDistributionEstimateRequest, opts ...grpc.CallOption) (*QueryNextFeeDistributionEstimateResponse, error) // QueryParams queries the ccv/consumer module parameters. QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + QueryProviderInfo(ctx context.Context, in *QueryProviderInfoRequest, opts ...grpc.CallOption) (*QueryProviderInfoResponse, error) } type queryClient struct { @@ -389,6 +558,15 @@ func (c *queryClient) QueryParams(ctx context.Context, in *QueryParamsRequest, o return out, nil } +func (c *queryClient) QueryProviderInfo(ctx context.Context, in *QueryProviderInfoRequest, opts ...grpc.CallOption) (*QueryProviderInfoResponse, error) { + out := new(QueryProviderInfoResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.consumer.v1.Query/QueryProviderInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // ConsumerGenesis queries the genesis state needed to start a consumer chain @@ -396,6 +574,7 @@ type QueryServer interface { QueryNextFeeDistribution(context.Context, *QueryNextFeeDistributionEstimateRequest) (*QueryNextFeeDistributionEstimateResponse, error) // QueryParams queries the ccv/consumer module parameters. QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + QueryProviderInfo(context.Context, *QueryProviderInfoRequest) (*QueryProviderInfoResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -408,6 +587,9 @@ func (*UnimplementedQueryServer) QueryNextFeeDistribution(ctx context.Context, r func (*UnimplementedQueryServer) QueryParams(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryParams not implemented") } +func (*UnimplementedQueryServer) QueryProviderInfo(ctx context.Context, req *QueryProviderInfoRequest) (*QueryProviderInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryProviderInfo not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -449,6 +631,24 @@ func _Query_QueryParams_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Query_QueryProviderInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryProviderInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryProviderInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.consumer.v1.Query/QueryProviderInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryProviderInfo(ctx, req.(*QueryProviderInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "interchain_security.ccv.consumer.v1.Query", HandlerType: (*QueryServer)(nil), @@ -461,6 +661,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryParams", Handler: _Query_QueryParams_Handler, }, + { + MethodName: "QueryProviderInfo", + Handler: _Query_QueryProviderInfo_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "interchain_security/ccv/consumer/v1/query.proto", @@ -646,6 +850,123 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QueryProviderInfoRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryProviderInfoRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProviderInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryProviderInfoResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryProviderInfoResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProviderInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Provider.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Consumer.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ChainInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ChainInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ChainInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelID) > 0 { + i -= len(m.ChannelID) + copy(dAtA[i:], m.ChannelID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelID))) + i-- + dAtA[i] = 0x22 + } + if len(m.ConnectionID) > 0 { + i -= len(m.ConnectionID) + copy(dAtA[i:], m.ConnectionID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConnectionID))) + i-- + dAtA[i] = 0x1a + } + if len(m.ClientID) > 0 { + i -= len(m.ClientID) + copy(dAtA[i:], m.ClientID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ClientID))) + i-- + dAtA[i] = 0x12 + } + if len(m.ChainID) > 0 { + i -= len(m.ChainID) + copy(dAtA[i:], m.ChainID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -733,6 +1054,53 @@ func (m *QueryParamsResponse) Size() (n int) { return n } +func (m *QueryProviderInfoRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryProviderInfoResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Consumer.Size() + n += 1 + l + sovQuery(uint64(l)) + l = m.Provider.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *ChainInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ClientID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ConnectionID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1243,6 +1611,350 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryProviderInfoRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProviderInfoRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProviderInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryProviderInfoResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProviderInfoResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProviderInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Consumer", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Consumer.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Provider", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Provider.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChainInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ChainInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChainInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/consumer/types/query.pb.gw.go b/x/ccv/consumer/types/query.pb.gw.go index 2aaa7d4b49..1151ab0b60 100644 --- a/x/ccv/consumer/types/query.pb.gw.go +++ b/x/ccv/consumer/types/query.pb.gw.go @@ -69,6 +69,24 @@ func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Ma } +func request_Query_QueryProviderInfo_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProviderInfoRequest + var metadata runtime.ServerMetadata + + msg, err := client.QueryProviderInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryProviderInfo_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProviderInfoRequest + var metadata runtime.ServerMetadata + + msg, err := server.QueryProviderInfo(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -121,6 +139,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryProviderInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryProviderInfo_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryProviderInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -202,6 +243,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryProviderInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryProviderInfo_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryProviderInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -209,10 +270,14 @@ var ( pattern_Query_QueryNextFeeDistribution_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "consumer", "next-fee-distribution"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "consumer", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryProviderInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "consumer", "provider-info"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( forward_Query_QueryNextFeeDistribution_0 = runtime.ForwardResponseMessage forward_Query_QueryParams_0 = runtime.ForwardResponseMessage + + forward_Query_QueryProviderInfo_0 = runtime.ForwardResponseMessage ) diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index 2a82561cf0..2f203a47ea 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -82,6 +82,7 @@ type ChannelKeeper interface { ) (sequence uint64, err error) WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, acknowledgement ibcexported.Acknowledgement) error ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error + GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, ibcexported.ConnectionI, error) } // PortKeeper defines the expected IBC port keeper From 3f4c54cd79c4e708f1c8d4b14ab4a84ff87432e1 Mon Sep 17 00:00:00 2001 From: bernd-m <43466467+bermuell@users.noreply.github.com> Date: Wed, 9 Aug 2023 11:43:12 +0200 Subject: [PATCH 34/38] test: Fix failing e2e test (#1186) * Tests: Fix failing e2e tests * Update tests/e2e/actions.go Co-authored-by: MSalopek * chore: appease lint rules * added check error message --------- Co-authored-by: MSalopek --- tests/e2e/actions.go | 12 +++++++++--- tests/e2e/steps_start_chains.go | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 8faca43c0a..faad8cb6f8 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1813,14 +1813,16 @@ type assignConsumerPubKeyAction struct { // reconfigureNode will change keys the node uses and restart reconfigureNode bool // executing the action should raise an error - expectError bool + expectError bool + expectedError string } func (tr TestRun) assignConsumerPubKey(action assignConsumerPubKeyAction, verbose bool) { valCfg := tr.validatorConfigs[action.validator] + // Note: to get error response reported back from this command '--gas auto' needs to be set. assignKey := fmt.Sprintf( - `%s tx provider assign-consensus-key %s '%s' --from validator%s --chain-id %s --home %s --node %s --gas 90000 --keyring-backend test -y -o json`, + `%s tx provider assign-consensus-key %s '%s' --from validator%s --chain-id %s --home %s --node %s --gas auto --keyring-backend test -y -o json`, tr.chainConfigs[chainID("provi")].binaryName, string(tr.chainConfigs[action.chain].chainId), action.consumerPubkey, @@ -1847,8 +1849,12 @@ func (tr TestRun) assignConsumerPubKey(action assignConsumerPubKeyAction, verbos } if action.expectError { + if err == nil || !strings.Contains(string(bz), action.expectedError) { + log.Fatalf("expected error not raised: expected: '%s', got '%s'", action.expectedError, (bz)) + } + if verbose { - fmt.Printf("got expected error during key assignment | err: %s \n", err.Error()) + fmt.Printf("got expected error during key assignment | err: %s | output: %s \n", err, string(bz)) } } diff --git a/tests/e2e/steps_start_chains.go b/tests/e2e/steps_start_chains.go index 15d0045760..6017a22641 100644 --- a/tests/e2e/steps_start_chains.go +++ b/tests/e2e/steps_start_chains.go @@ -88,6 +88,7 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint consumerPubkey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}`, reconfigureNode: false, expectError: true, + expectedError: "a validator has assigned the consumer key already: consumer key is already in use by a validator", }, state: State{}, }, @@ -100,6 +101,7 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint consumerPubkey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}`, reconfigureNode: false, expectError: true, + expectedError: "a validator has assigned the consumer key already: consumer key is already in use by a validator", }, state: State{ chainID(consumerName): ChainState{ From 4045b6a8fc9664c06156bdb4b98fb04aa8aa513f Mon Sep 17 00:00:00 2001 From: MSalopek Date: Wed, 9 Aug 2023 12:56:28 +0200 Subject: [PATCH 35/38] test: use cometmock in CI; run parallel jobs (#1181) * test: reduce number of happyPath steps * test: reduce number of happyPath steps * test: update steps after opt-out * test: update steps and fix lint * tests: add cometmock tests * chore: Fix and enable CometMock in e2e tests (#1184) * Add make time.sleep WaitTime and wait block after waiting time * Uncomment verbose error check * Fix spacing * Make format * Add make target for CometMock e2e tests * Add CometMock job to automated tests * tests: revert reducing happyPath steps count * tests: revert reducing happyPath steps count * Fix --gas auto with CometMock --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Co-authored-by: Philip Offtermatt --- .github/workflows/automated-tests.yml | 26 ++++++++++++++++++++++++++ Makefile | 5 +++++ tests/e2e/actions.go | 15 ++++++++++++--- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/.github/workflows/automated-tests.yml b/.github/workflows/automated-tests.yml index d48b8ef817..a9b594f53d 100644 --- a/.github/workflows/automated-tests.yml +++ b/.github/workflows/automated-tests.yml @@ -28,5 +28,31 @@ jobs: run: make proto-check - name: Unit, integration and difference tests run: go test ./... + E2E_Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + lfs: true + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" - name: E2E tests run: make test-e2e-short + Cometmock_Tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + lfs: true + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" + - name: E2E tests + run: make test-e2e-short-cometmock diff --git a/Makefile b/Makefile index 28c02f7c85..7ab6a4fa24 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,11 @@ test-diff: test-e2e-short: go run ./tests/e2e/... --happy-path-only +# run only happy path E2E tests with cometmock +# this set of traces does not test equivocation but it does check downtime +test-e2e-short-cometmock: + go run ./tests/e2e/... --short-happy-path --use-cometmock --use-gorelayer + # run full E2E tests in sequence (including multiconsumer) test-e2e-multi-consumer: go run ./tests/e2e/... --include-multi-consumer diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index faad8cb6f8..2cae615f70 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -538,7 +538,7 @@ func (tr *TestRun) voteGovProposal( wg.Wait() // wait for inclusion in a block -> '--broadcast-mode block' is deprecated tr.waitBlocks(action.chain, 1, 10*time.Second) - time.Sleep(time.Duration(tr.chainConfigs[action.chain].votingWaitTime) * time.Second) + tr.WaitTime(time.Duration(tr.chainConfigs[action.chain].votingWaitTime) * time.Second) } type startConsumerChainAction struct { @@ -1821,8 +1821,13 @@ func (tr TestRun) assignConsumerPubKey(action assignConsumerPubKeyAction, verbos valCfg := tr.validatorConfigs[action.validator] // Note: to get error response reported back from this command '--gas auto' needs to be set. + gas := "auto" + // Unfortunately, --gas auto does not work with CometMock. so when using CometMock, just use --gas 9000000 then + if tr.useCometmock { + gas = "9000000" + } assignKey := fmt.Sprintf( - `%s tx provider assign-consensus-key %s '%s' --from validator%s --chain-id %s --home %s --node %s --gas auto --keyring-backend test -y -o json`, + `%s tx provider assign-consensus-key %s '%s' --from validator%s --chain-id %s --home %s --node %s --gas %s --keyring-backend test -y -o json`, tr.chainConfigs[chainID("provi")].binaryName, string(tr.chainConfigs[action.chain].chainId), action.consumerPubkey, @@ -1830,6 +1835,7 @@ func (tr TestRun) assignConsumerPubKey(action assignConsumerPubKeyAction, verbos tr.chainConfigs[chainID("provi")].chainId, tr.getValidatorHome(chainID("provi"), action.validator), tr.getValidatorNode(chainID("provi"), action.validator), + gas, ) //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. @@ -1848,7 +1854,7 @@ func (tr TestRun) assignConsumerPubKey(action assignConsumerPubKeyAction, verbos log.Fatalf("unexpected error during key assignment - output: %s, err: %s", string(bz), err) } - if action.expectError { + if action.expectError && !tr.useCometmock { // error report ony works with --gas auto, which does not work with CometMock, so ignore if err == nil || !strings.Contains(string(bz), action.expectedError) { log.Fatalf("expected error not raised: expected: '%s', got '%s'", action.expectedError, (bz)) } @@ -1972,6 +1978,8 @@ func (tr TestRun) GetPathNameForGorelayer(chainA, chainB chainID) string { } // WaitTime waits for the given duration. +// To make sure that the new timestamp is visible on-chain, it also waits until at least one block has been +// produced on each chain after waiting. // The CometMock version of this takes a pointer to the TestRun as it needs to manipulate // information in the testrun that stores how much each chain has waited, to keep times in sync. // Be careful that all functions calling WaitTime should therefore also take a pointer to the TestRun. @@ -1985,6 +1993,7 @@ func (tr *TestRun) WaitTime(duration time.Duration) { continue } tr.AdvanceTimeForChain(chain, duration) + tr.waitBlocks(chain, 1, 2*time.Second) } } } From 5cb2ffaec2771b2a52f7916c05c6014a42accdcd Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Thu, 10 Aug 2023 12:23:11 +0200 Subject: [PATCH 36/38] chore: Add integration for equivocation with CometMock (#1192) Add support for equivocation --- Makefile | 2 +- tests/e2e/actions.go | 58 +++++++++++++++++++++++++++++--------------- tests/e2e/main.go | 11 ++++----- tests/e2e/steps.go | 4 ++- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index 7ab6a4fa24..edbacbc0e0 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ test-e2e-short: # run only happy path E2E tests with cometmock # this set of traces does not test equivocation but it does check downtime test-e2e-short-cometmock: - go run ./tests/e2e/... --short-happy-path --use-cometmock --use-gorelayer + go run ./tests/e2e/... --cometmock-happy-path --use-cometmock --use-gorelayer # run full E2E tests in sequence (including multiconsumer) test-e2e-multi-consumer: diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 2cae615f70..e5ac465aa0 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1604,18 +1604,7 @@ func (tr TestRun) setValidatorDowntime(chain chainID, validator validatorID, dow if tr.useCometmock { // send set_signing_status either to down or up for validator - var validatorAddress string - if chain == chainID("provi") { - validatorAddress = tr.getValidatorKeyAddressFromString(tr.validatorConfigs[validator].privValidatorKey) - } else { - var valAddressString string - if tr.validatorConfigs[validator].useConsumerKey { - valAddressString = tr.validatorConfigs[validator].consumerPrivValidatorKey - } else { - valAddressString = tr.validatorConfigs[validator].privValidatorKey - } - validatorAddress = tr.getValidatorKeyAddressFromString(valAddressString) - } + validatorAddress := tr.GetValidatorAddress(chain, validator) method := "set_signing_status" params := fmt.Sprintf(`{"private_key_address":"%s","status":"%s"}`, validatorAddress, lastArg) @@ -1648,6 +1637,22 @@ func (tr TestRun) setValidatorDowntime(chain chainID, validator validatorID, dow } } +func (tr TestRun) GetValidatorAddress(chain chainID, validator validatorID) string { + var validatorAddress string + if chain == chainID("provi") { + validatorAddress = tr.getValidatorKeyAddressFromString(tr.validatorConfigs[validator].privValidatorKey) + } else { + var valAddressString string + if tr.validatorConfigs[validator].useConsumerKey { + valAddressString = tr.validatorConfigs[validator].consumerPrivValidatorKey + } else { + valAddressString = tr.validatorConfigs[validator].privValidatorKey + } + validatorAddress = tr.getValidatorKeyAddressFromString(valAddressString) + } + return validatorAddress +} + type unjailValidatorAction struct { provider chainID validator validatorID @@ -1795,15 +1800,28 @@ func (tr TestRun) invokeDoublesignSlash( action doublesignSlashAction, verbose bool, ) { - chainConfig := tr.chainConfigs[action.chain] - //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. - bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, "/bin/bash", - "/testnet-scripts/cause-doublesign.sh", chainConfig.binaryName, string(action.validator), - string(chainConfig.chainId), chainConfig.ipPrefix).CombinedOutput() - if err != nil { - log.Fatal(err, "\n", string(bz)) + if !tr.useCometmock { + chainConfig := tr.chainConfigs[action.chain] + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, "/bin/bash", + "/testnet-scripts/cause-doublesign.sh", chainConfig.binaryName, string(action.validator), + string(chainConfig.chainId), chainConfig.ipPrefix).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + tr.waitBlocks("provi", 10, 2*time.Minute) + } else { // tr.useCometMock + validatorAddress := tr.GetValidatorAddress(action.chain, action.validator) + + method := "cause_double_sign" + params := fmt.Sprintf(`{"private_key_address":"%s"}`, validatorAddress) + + address := tr.getQueryNodeRPCAddress(action.chain) + + tr.curlJsonRPCRequest(method, params, address) + tr.waitBlocks(action.chain, 1, 10*time.Second) + return } - tr.waitBlocks("provi", 10, 2*time.Minute) } type assignConsumerPubKeyAction struct { diff --git a/tests/e2e/main.go b/tests/e2e/main.go index 9943d6f9bd..58bb065c26 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -16,12 +16,11 @@ import ( ) var ( - verbose = flag.Bool("verbose", false, "turn verbose logging on/off") - happyPathOnly = flag.Bool("happy-path-only", false, "run happy path tests only") - shortHappyPathOnly = flag.Bool("short-happy-path", false, `run abridged happy path tests only. + verbose = flag.Bool("verbose", false, "turn verbose logging on/off") + happyPathOnly = flag.Bool("happy-path-only", false, "run happy path tests only") + cometmockCompatibleHappyPath = flag.Bool("cometmock-happy-path", false, `run cometmock compatible happy path tests only. This is like the happy path, but skips steps that involve starting or stopping nodes for the same chain outside of the chain setup or teardown. -In particular, this skips steps related to downtime and double signing. This is suited for CometMock+Gorelayer testing`) includeMultiConsumer = flag.Bool("include-multi-consumer", false, "include multiconsumer tests in run") parallel = flag.Bool("parallel", false, "run all tests in parallel") @@ -42,10 +41,10 @@ var ( func main() { flag.Parse() - if shortHappyPathOnly != nil && *shortHappyPathOnly { + if cometmockCompatibleHappyPath != nil && *cometmockCompatibleHappyPath { fmt.Println("=============== running short happy path only ===============") tr := DefaultTestRun() - tr.Run(shortHappyPathSteps, *localSdkPath, *useGaia, *gaiaTag) + tr.Run(cometmockCompatibleHappyPathSteps, *localSdkPath, *useGaia, *gaiaTag) return } diff --git a/tests/e2e/steps.go b/tests/e2e/steps.go index aa08426103..770ac45dda 100644 --- a/tests/e2e/steps.go +++ b/tests/e2e/steps.go @@ -31,12 +31,14 @@ var happyPathSteps = concatSteps( stepsStopChain("consu", 4), // stop chain ) -var shortHappyPathSteps = concatSteps( +var cometmockCompatibleHappyPathSteps = concatSteps( stepsStartChains([]string{"consu"}, false), stepsDelegate("consu"), stepsUnbond("consu"), stepsRedelegateShort("consu"), stepsDowntime("consu"), + stepsRejectEquivocationProposal("consu", 2), // prop to tombstone bob is rejected + stepsDoubleSignOnProviderAndConsumer("consu"), // carol double signs on provider, bob double signs on consumer stepsStartRelayer(), stepsConsumerRemovalPropNotPassing("consu", 2), // submit removal prop but vote no on it - chain should stay stepsStopChain("consu", 3), // stop chain From fbbac82181dc43d76193aab694dd595079117cbb Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Thu, 10 Aug 2023 09:08:17 -0700 Subject: [PATCH 37/38] feat!: throttle with retries, consumer changes (#1024) * wip * wip * bouncing slash constructor and nits * UT * define cross chain ack enum * wip * tests * tests * update genesis tests * comments * migration and changelog * migration test * lints * merge fixes * clean * Update ccv.pb.go * add to ADR * address some PR comments * rebuild protos * v3s * lint * regen pbs * refactor for simplicity * comment * changes with slash record type * wip * Update ccv.pb.go * Update ccv.pb.go * update SendPackets and test * test packet sending permitted * test for OnAckPacket * note on FSM design * CRUD UT * packet sending permitted UT * nits * Update throttle_retry.go * v1 result and change tests * Update relay.go * expectation func * reg test * Update CHANGELOG.md * lints * doc on upgrade order * small updates * vsc matured handled res * handle vsc matured acks * adjust TestSendPacketsDeletion * Update relay_test.go * fix integration test * fix send slash packet deletion test * fix TestConsumerPacketSendExpiredClient * lint * fix more tests * final test fixes * disable diff tests * smalls * Update steps_downtime.go * Update slashing.go * Update CHANGELOG.md * Update x/ccv/consumer/keeper/throttle_retry.go Co-authored-by: Marius Poke * Update throttle_retry.go * Update x/ccv/consumer/keeper/throttle_retry.go Co-authored-by: Marius Poke * docstrings * comment * DeleteHeadOfPendingPackets unit tests * fix dup deletion * better comment * const * rm todo and unneeded call * linting is very important * break instead of return * return instead of just print * fix test * fix slashing test * comment * camel case * FSM event explanation --------- Co-authored-by: Marius Poke --- CHANGELOG.md | 1 + docs/docs/adrs/adr-008-throttle-retries.md | 9 +- .../ccv/consumer/v1/consumer.proto | 8 + tests/difference/core/driver/core_test.go | 10 +- tests/e2e/steps_downtime.go | 43 ++- tests/integration/expired_client.go | 24 +- tests/integration/slashing.go | 72 +++- tests/integration/throttle_retry.go | 150 +++++++++ testutil/integration/debug_test.go | 8 + x/ccv/consumer/keeper/keeper.go | 11 + x/ccv/consumer/keeper/keeper_test.go | 27 ++ x/ccv/consumer/keeper/relay.go | 49 ++- x/ccv/consumer/keeper/relay_test.go | 225 +++++++++++-- x/ccv/consumer/keeper/throttle_retry.go | 112 ++++++ x/ccv/consumer/keeper/throttle_retry_test.go | 91 +++++ x/ccv/consumer/types/consumer.pb.go | 318 +++++++++++++++--- x/ccv/consumer/types/keys.go | 8 + x/ccv/consumer/types/keys_test.go | 2 + x/ccv/consumer/types/throttle_retry.go | 11 + x/ccv/provider/keeper/relay.go | 6 +- x/ccv/types/ccv.go | 14 + 21 files changed, 1077 insertions(+), 122 deletions(-) create mode 100644 tests/integration/throttle_retry.go create mode 100644 x/ccv/consumer/keeper/throttle_retry.go create mode 100644 x/ccv/consumer/keeper/throttle_retry_test.go create mode 100644 x/ccv/consumer/types/throttle_retry.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 5003260943..f5c4d215d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Add an entry to the unreleased section whenever merging a PR to main that is not targeted at a specific release. These entries will eventually be included in a release. * `[x/ccv/provider]` (fix) [#1076](https://github.com/cosmos/interchain-security/pull/1076) Add `InitTimeoutTimestamps` and `ExportedVscSendTimestamps` to exported genesis. +* (feat!) [#1024](https://github.com/cosmos/interchain-security/pull/1024) throttle with retries, consumer changes * (fix!) revert consumer packet data changes from #1037 [#1150](https://github.com/cosmos/interchain-security/pull/1150) * (fix!) proper deletion of pending packets [#1146](https://github.com/cosmos/interchain-security/pull/1146) * (feat!) optimize pending packets storage on consumer, with migration! [#1037](https://github.com/cosmos/interchain-security/pull/1037) diff --git a/docs/docs/adrs/adr-008-throttle-retries.md b/docs/docs/adrs/adr-008-throttle-retries.md index 134214fffb..1faf7bd7ee 100644 --- a/docs/docs/adrs/adr-008-throttle-retries.md +++ b/docs/docs/adrs/adr-008-throttle-retries.md @@ -9,6 +9,7 @@ title: Throttle with retries * 6/9/23: Initial draft * 6/22/23: added note on consumer pending packets storage optimization +* 7/14/23: Added note on upgrade order ## Status @@ -47,6 +48,8 @@ With the behavior described, we maintain very similar behavior to the current th In the normal case, when no or a few slash packets are being sent, the VSCMaturedPackets will not be delayed, and hence unbonding will not be delayed. +For implementation of this design, see [throttle_retry.go](../../../x/ccv/consumer/keeper/throttle_retry.go). + ### Consumer pending packets storage optimization In addition to the mentioned consumer changes above. An optimization will need to be made to the consumer's pending packets storage to properly implement the feature from this ADR. @@ -86,9 +89,11 @@ If a consumer sends VSCMatured packets too leniently: The consumer is malicious If a consumer blocks the sending of VSCMatured packets: The consumer is malicious and blocking vsc matured packets that should have been sent. This will block unbonding only up until the VSC timeout period has elapsed. At that time, the consumer is removed. Again the malicious behavior only creates a negative outcome for the chain that is being malicious. -### Splitting of PRs +### Splitting of PRs and Upgrade Order + +This feature will implement consumer changes in [#1024](https://github.com/cosmos/interchain-security/pull/1024). Note these changes should be deployed to prod for all consumers before the provider changes are deployed to prod. That is the consumer changes in #1024 are compatible with the current ("v1") provider implementation of throttling that's running on the Cosmos Hub as of July 2023. -We could split this feature into two PRs, one affecting the consumer and one affecting the provider, along with a third PR which could setup a clever way to upgrade the provider in multiple steps, ensuring that queued slash packets at upgrade time are handled properly. +Once all consumers have deployed the changes in #1024, the provider changes from (TBD) can be deployed to prod, fully enabling v2 throttling. ## Consequences diff --git a/proto/interchain_security/ccv/consumer/v1/consumer.proto b/proto/interchain_security/ccv/consumer/v1/consumer.proto index 97ba14f6da..2b4b6f88c3 100644 --- a/proto/interchain_security/ccv/consumer/v1/consumer.proto +++ b/proto/interchain_security/ccv/consumer/v1/consumer.proto @@ -89,3 +89,11 @@ message MaturingVSCPacket { google.protobuf.Timestamp maturity_time = 2 [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; } + +// A record storing the state of a slash packet sent to the provider chain +// which may bounce back and forth until handled by the provider. +message SlashRecord { + bool waiting_on_reply = 1; + google.protobuf.Timestamp send_time = 2 + [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; +} \ No newline at end of file diff --git a/tests/difference/core/driver/core_test.go b/tests/difference/core/driver/core_test.go index 12192eb8e4..ec97fbf9c1 100644 --- a/tests/difference/core/driver/core_test.go +++ b/tests/difference/core/driver/core_test.go @@ -2,7 +2,6 @@ package core import ( "fmt" - "testing" "time" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" @@ -331,9 +330,12 @@ func (s *CoreSuite) TestTraces() { fmt.Println("Shortest [traceIx, actionIx]:", shortest, shortestLen) } -func TestCoreSuite(t *testing.T) { - suite.Run(t, new(CoreSuite)) -} +// TODO: diff tests will eventually be replaced by quint tests, and all this code could then be deleted. +// Until that decision is finalized, we'll just comment out the top-level test. + +// func TestCoreSuite(t *testing.T) { +// suite.Run(t, new(CoreSuite)) +// } // SetupTest sets up the test suite in a 'zero' state which matches // the initial state in the model. diff --git a/tests/e2e/steps_downtime.go b/tests/e2e/steps_downtime.go index 74cb349000..e6d320bec1 100644 --- a/tests/e2e/steps_downtime.go +++ b/tests/e2e/steps_downtime.go @@ -278,7 +278,7 @@ func stepsThrottledDowntime(consumerName string) []Step { validator: validatorID("bob"), }, state: State{ - // powers not affected on either chain yet + // slash packet queued on consumer, but powers not affected on either chain yet chainID("provi"): ChainState{ ValPowers: &map[validatorID]uint{ validatorID("alice"): 511, @@ -295,6 +295,39 @@ func stepsThrottledDowntime(consumerName string) []Step { }, }, }, + // Relay packets so bob is jailed on provider, + // and consumer receives ack that provider recv the downtime slash. + // The latter is necessary for the consumer to send the second downtime slash. + { + action: relayPacketsAction{ + chainA: chainID("provi"), + chainB: chainID(consumerName), + port: "provider", + channel: 0, + }, + state: State{ + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 0, // bob is jailed + validatorID("carol"): 500, + }, + // no provider throttling engaged yet + GlobalSlashQueueSize: uintPointer(0), + ConsumerChainQueueSizes: &map[chainID]uint{ + chainID(consumerName): uint(0), + }, + }, + chainID(consumerName): ChainState{ + // VSC packet applying jailing is not yet relayed to consumer + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + }, + }, { action: downtimeSlashAction{ chain: chainID(consumerName), @@ -305,11 +338,12 @@ func stepsThrottledDowntime(consumerName string) []Step { chainID("provi"): ChainState{ ValPowers: &map[validatorID]uint{ validatorID("alice"): 511, - validatorID("bob"): 500, + validatorID("bob"): 0, validatorID("carol"): 500, }, }, chainID(consumerName): ChainState{ + // VSC packet applying jailing is not yet relayed to consumer ValPowers: &map[validatorID]uint{ validatorID("alice"): 511, validatorID("bob"): 500, @@ -338,10 +372,9 @@ func stepsThrottledDowntime(consumerName string) []Step { }, }, chainID(consumerName): ChainState{ - // no updates received on consumer ValPowers: &map[validatorID]uint{ validatorID("alice"): 511, - validatorID("bob"): 500, + validatorID("bob"): 0, validatorID("carol"): 500, }, }, @@ -373,7 +406,7 @@ func stepsThrottledDowntime(consumerName string) []Step { // no updates received on consumer ValPowers: &map[validatorID]uint{ validatorID("alice"): 511, - validatorID("bob"): 500, + validatorID("bob"): 0, validatorID("carol"): 500, }, }, diff --git a/tests/integration/expired_client.go b/tests/integration/expired_client.go index 53863d2881..2a8babacfa 100644 --- a/tests/integration/expired_client.go +++ b/tests/integration/expired_client.go @@ -139,20 +139,34 @@ func (s *CCVTestSuite) TestConsumerPacketSendExpiredClient() { // check that the packets were added to the list of pending data packets consumerPackets = consumerKeeper.GetPendingPackets(s.consumerCtx()) s.Require().NotEmpty(consumerPackets) + // At this point we expect 4 packets, two vsc matured packets and two trailing slash packets s.Require().Len(consumerPackets, 4, "unexpected number of pending data packets") // upgrade expired client to the consumer upgradeExpiredClient(s, Provider) - // go to next block to trigger SendPendingDataPackets + // go to next block to trigger SendPendingPackets s.consumerChain.NextBlock() - // check that the list of pending data packets is emptied + // Check that the leading vsc matured packets were sent and no longer pending consumerPackets = consumerKeeper.GetPendingPackets(s.consumerCtx()) - s.Require().Empty(consumerPackets) + s.Require().Len(consumerPackets, 2, "unexpected number of pending data packets") + + // relay committed packets from consumer to provider, first slash packet should be committed + relayAllCommittedPackets(s, s.consumerChain, s.path, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, 3) // two vsc matured + one slash + + // First slash has been acked, now only the second slash packet should remain as pending + consumerPackets = consumerKeeper.GetPendingPackets(s.consumerCtx()) + s.Require().Len(consumerPackets, 1, "unexpected number of pending data packets") - // relay all packet from consumer to provider - relayAllCommittedPackets(s, s.consumerChain, s.path, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, 4) + // go to next block to trigger SendPendingPackets + s.consumerChain.NextBlock() + + // relay committed packets from consumer to provider, only second slash packet should be committed + relayAllCommittedPackets(s, s.consumerChain, s.path, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, 1) // one slash + + consumerPackets = consumerKeeper.GetPendingPackets(s.consumerCtx()) + s.Require().Empty(consumerPackets, "pending data packets found") // check that everything works // - bond more tokens on provider to change validator powers diff --git a/tests/integration/slashing.go b/tests/integration/slashing.go index 2bc960fd03..1ce7d11db8 100644 --- a/tests/integration/slashing.go +++ b/tests/integration/slashing.go @@ -247,14 +247,34 @@ func (s *CCVTestSuite) TestSlashPacketAcknowledgement() { s.SetupCCVChannel(s.path) s.SetupTransferChannel() - packet := channeltypes.NewPacket([]byte{}, 1, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, + // Mock a proper slash packet from consumer + spd := keepertestutil.GetNewSlashPacketData() + + // We don't want truly randomized fields, infraction needs to be specified + if spd.Infraction == stakingtypes.Infraction_INFRACTION_UNSPECIFIED { + spd.Infraction = stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN + } + cpd := ccv.NewConsumerPacketData(ccv.SlashPacket, + &ccv.ConsumerPacketData_SlashPacketData{ + SlashPacketData: &spd, + }, + ) + packet := channeltypes.NewPacket(cpd.GetBytes(), // Consumer always sends v1 packet data + 1, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, ccv.ProviderPortID, s.path.EndpointB.ChannelID, clienttypes.Height{}, 0) - ack := providerKeeper.OnRecvSlashPacket(s.providerCtx(), packet, - keepertestutil.GetNewSlashPacketData()) - s.Require().NotNil(ack) + // Map infraction height on provider so validation passes and provider returns valid ack result + providerKeeper.SetValsetUpdateBlockHeight(s.providerCtx(), spd.ValsetUpdateId, 47923) + + exportedAck := providerKeeper.OnRecvSlashPacket(s.providerCtx(), packet, spd) + s.Require().NotNil(exportedAck) - err := consumerKeeper.OnAcknowledgementPacket(s.consumerCtx(), packet, channeltypes.NewResultAcknowledgement(ack.Acknowledgement())) + // Unmarshal ack to struct that's compatible with consumer. IBC does this automatically + ack := channeltypes.Acknowledgement{} + err := channeltypes.SubModuleCdc.UnmarshalJSON(exportedAck.Acknowledgement(), &ack) + s.Require().NoError(err) + + err = consumerKeeper.OnAcknowledgementPacket(s.consumerCtx(), packet, ack) s.Require().NoError(err) err = consumerKeeper.OnAcknowledgementPacket(s.consumerCtx(), packet, ccv.NewErrorAcknowledgementWithLog(s.consumerCtx(), fmt.Errorf("another error"))) @@ -492,9 +512,15 @@ func (suite *CCVTestSuite) TestValidatorDowntime() { // clear queue, commit packets suite.consumerApp.GetConsumerKeeper().SendPackets(ctx) - // check queue was cleared + // Check slash record is created + slashRecord, found := suite.consumerApp.GetConsumerKeeper().GetSlashRecord(suite.consumerCtx()) + suite.Require().True(found, "slash record not found") + suite.Require().True(slashRecord.WaitingOnReply) + suite.Require().Equal(slashRecord.SendTime, suite.consumerCtx().BlockTime()) + + // check queue is not cleared, since no ack has been received from provider pendingPackets = suite.consumerApp.GetConsumerKeeper().GetPendingPackets(ctx) - suite.Require().Empty(pendingPackets, "pending packets NOT empty") + suite.Require().Len(pendingPackets, 1, "pending packets len should be 1 is %d", len(pendingPackets)) // verify that the slash packet was sent gotCommit := consumerIBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, ccv.ConsumerPortID, channelID, seq) @@ -579,9 +605,15 @@ func (suite *CCVTestSuite) TestValidatorDoubleSigning() { // clear queue, commit packets suite.consumerApp.GetConsumerKeeper().SendPackets(ctx) - // check queue was cleared + // Check slash record is created + slashRecord, found := suite.consumerApp.GetConsumerKeeper().GetSlashRecord(suite.consumerCtx()) + suite.Require().True(found, "slash record not found") + suite.Require().True(slashRecord.WaitingOnReply) + suite.Require().Equal(slashRecord.SendTime, suite.consumerCtx().BlockTime()) + + // check queue is not cleared, since no ack has been received from provider pendingPackets = suite.consumerApp.GetConsumerKeeper().GetPendingPackets(ctx) - suite.Require().Empty(pendingPackets, "pending packets NOT empty") + suite.Require().Len(pendingPackets, 1, "pending packets len should be 1 is %d", len(pendingPackets)) // check slash packet is sent gotCommit := suite.consumerApp.GetIBCKeeper().ChannelKeeper.GetPacketCommitment(ctx, ccv.ConsumerPortID, channelID, seq) @@ -644,10 +676,12 @@ func (suite *CCVTestSuite) TestQueueAndSendSlashPacket() { // establish ccv channel by sending an empty VSC packet to consumer endpoint suite.SendEmptyVSCPacket() - // check that each pending data packet is sent once + // check that each pending data packet is sent once, as long as the prev slash packet was relayed/acked. + // Note that consumer throttling blocks packet sending until a slash packet is successfully acked by the provider. for i := 0; i < 12; i++ { commit := consumerIBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, ccv.ConsumerPortID, channelID, seq+uint64(i)) suite.Require().NotNil(commit) + relayAllCommittedPackets(suite, suite.consumerChain, suite.path, ccv.ConsumerPortID, channelID, 1) } // check that outstanding downtime flags @@ -657,8 +691,8 @@ func (suite *CCVTestSuite) TestQueueAndSendSlashPacket() { suite.Require().True(consumerKeeper.OutstandingDowntime(ctx, consAddr)) } - // send all pending packets - only slash packets should be queued in this test - consumerKeeper.SendPackets(ctx) + // SendPackets method should have already been called during + // endblockers in relayAllCommittedPackets above // check that pending data packets got cleared dataPackets = consumerKeeper.GetPendingPackets(ctx) @@ -676,6 +710,10 @@ func (suite *CCVTestSuite) TestCISBeforeCCVEstablished() { pendingPackets := consumerKeeper.GetPendingPackets(suite.consumerCtx()) suite.Require().Len(pendingPackets, 0) + // No slash record found (no slash sent) + _, found := consumerKeeper.GetSlashRecord(suite.consumerCtx()) + suite.Require().False(found) + consumerKeeper.SlashWithInfractionReason(suite.consumerCtx(), []byte{0x01, 0x02, 0x3}, 66, 4324, sdk.MustNewDecFromStr("0.05"), stakingtypes.Infraction_INFRACTION_DOWNTIME) @@ -683,6 +721,10 @@ func (suite *CCVTestSuite) TestCISBeforeCCVEstablished() { pendingPackets = consumerKeeper.GetPendingPackets(suite.consumerCtx()) suite.Require().Len(pendingPackets, 1) + // but slash packet is not yet sent + _, found = consumerKeeper.GetSlashRecord(suite.consumerCtx()) + suite.Require().False(found) + // Pass 5 blocks, confirming the consumer doesn't panic for i := 0; i < 5; i++ { suite.consumerChain.NextBlock() @@ -698,6 +740,8 @@ func (suite *CCVTestSuite) TestCISBeforeCCVEstablished() { // Pass one more block, and confirm the packet is sent now that ccv channel is established suite.consumerChain.NextBlock() - pendingPackets = consumerKeeper.GetPendingPackets(suite.consumerCtx()) - suite.Require().Len(pendingPackets, 0) + + // Slash record should now be found (slash sent) + _, found = consumerKeeper.GetSlashRecord(suite.consumerCtx()) + suite.Require().True(found) } diff --git a/tests/integration/throttle_retry.go b/tests/integration/throttle_retry.go new file mode 100644 index 0000000000..ae15aac977 --- /dev/null +++ b/tests/integration/throttle_retry.go @@ -0,0 +1,150 @@ +package integration + +import ( + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + provider "github.com/cosmos/interchain-security/v3/x/ccv/provider" + providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" +) + +// TestSlashRetries tests the throttling v2 retry logic. Without provider changes, +// the consumer will queue up a slash packet, the provider will return a v1 result, +// and the consumer will never need to retry. +// +// Once provider changes are made (slash packet queuing is removed), the consumer may retry packets +// via new result acks from the provider. +// +// TODO: This test will need updating once provider changes are made. +func (s *CCVTestSuite) TestSlashRetries() { + s.SetupAllCCVChannels() + s.setupValidatorPowers() + + // + // Provider setup + // + providerKeeper := s.providerApp.GetProviderKeeper() + providerModule := provider.NewAppModule(&providerKeeper, s.providerApp.GetSubspace(providertypes.ModuleName)) + // Initialize slash meter + providerKeeper.InitializeSlashMeter(s.providerCtx()) + // Assert that we start out with no jailings + providerStakingKeeper := s.providerApp.GetTestStakingKeeper() + vals := providerStakingKeeper.GetAllValidators(s.providerCtx()) + for _, val := range vals { + s.Require().False(val.IsJailed()) + } + // Setup signing info for jailings + s.setDefaultValSigningInfo(*s.providerChain.Vals.Validators[1]) + + // + // Consumer setup + // + consumerKeeper := s.consumerApp.GetConsumerKeeper() + // Assert no slash record exists + _, found := consumerKeeper.GetSlashRecord(s.consumerCtx()) + s.Require().False(found) + + // + // Test section: See FSM explanation in throttle_retry.go + // + + // Construct a mock slash packet from consumer + tmval1 := s.providerChain.Vals.Validators[1] + packet1 := s.constructSlashPacketFromConsumer(s.getFirstBundle(), *tmval1, stakingtypes.Infraction_INFRACTION_DOWNTIME, 1) + + // Mock the sending of the packet on consumer + consumerKeeper.AppendPendingPacket(s.consumerCtx(), ccvtypes.SlashPacket, + &ccvtypes.ConsumerPacketData_SlashPacketData{ + SlashPacketData: &ccvtypes.SlashPacketData{}, + }, + ) + consumerKeeper.UpdateSlashRecordOnSend(s.consumerCtx()) + slashRecord, found := consumerKeeper.GetSlashRecord(s.consumerCtx()) + s.Require().True(found) + s.Require().True(slashRecord.WaitingOnReply) + s.Require().Len(consumerKeeper.GetPendingPackets(s.consumerCtx()), 1) + + // Recv packet on provider and assert ack. Provider should return v1 result. + ack := providerModule.OnRecvPacket(s.providerCtx(), packet1, nil) + expectedv1Ack := channeltypes.NewResultAcknowledgement([]byte(ccvtypes.V1Result)) + s.Require().Equal(expectedv1Ack.Acknowledgement(), ack.Acknowledgement()) + + // Couple blocks pass on provider for provider staking keeper to process jailing + s.providerChain.NextBlock() + s.providerChain.NextBlock() + + // Default slash meter replenish fraction is 0.05, so packet should be handled on provider. + vals = s.providerApp.GetTestStakingKeeper().GetAllValidators(s.providerCtx()) + s.Require().True(vals[1].IsJailed()) + s.Require().Equal(int64(0), + s.providerApp.GetTestStakingKeeper().GetLastValidatorPower(s.providerCtx(), vals[1].GetOperator())) + s.Require().Equal(uint64(0), providerKeeper.GetThrottledPacketDataSize(s.providerCtx(), + s.getFirstBundle().Chain.ChainID)) + + // Now slash meter should be negative on provider + s.Require().True(s.providerApp.GetProviderKeeper().GetSlashMeter(s.providerCtx()).IsNegative()) + + // Apply ack back on consumer + ackForConsumer := expectedv1Ack + err := consumerKeeper.OnAcknowledgementPacket(s.consumerCtx(), packet1, ackForConsumer) + s.Require().NoError(err) + + // Slash record should have been deleted, head of pending packets should have been popped + // Since provider has handled the packet + _, found = consumerKeeper.GetSlashRecord(s.consumerCtx()) + s.Require().False(found) + s.Require().Empty(consumerKeeper.GetPendingPackets(s.consumerCtx())) + + // pass two blocks on provider and consumer for good measure + s.providerChain.NextBlock() + s.providerChain.NextBlock() + s.consumerChain.NextBlock() + s.consumerChain.NextBlock() + + // Construct and mock the sending of a second packet on consumer + tmval2 := s.providerChain.Vals.Validators[2] + packet2 := s.constructSlashPacketFromConsumer(s.getFirstBundle(), *tmval2, stakingtypes.Infraction_INFRACTION_DOWNTIME, 1) + + consumerKeeper.AppendPendingPacket(s.consumerCtx(), ccvtypes.SlashPacket, + &ccvtypes.ConsumerPacketData_SlashPacketData{ + SlashPacketData: &ccvtypes.SlashPacketData{}, + }, + ) + consumerKeeper.UpdateSlashRecordOnSend(s.consumerCtx()) + slashRecord, found = consumerKeeper.GetSlashRecord(s.consumerCtx()) + s.Require().True(found) + s.Require().True(slashRecord.WaitingOnReply) + s.Require().Len(consumerKeeper.GetPendingPackets(s.consumerCtx()), 1) + + // Recv 2nd slash packet on provider for different validator. + // Provider should return the same v1 result ack even tho the packet was queued. + ack = providerModule.OnRecvPacket(s.providerCtx(), packet2, nil) + expectedv1Ack = channeltypes.NewResultAcknowledgement([]byte(ccvtypes.V1Result)) + s.Require().Equal(expectedv1Ack.Acknowledgement(), ack.Acknowledgement()) + + // Couple blocks pass on provider for staking keeper to process jailings + s.providerChain.NextBlock() + s.providerChain.NextBlock() + + // Val shouldn't be jailed on provider. Slash packet was queued + s.Require().False(vals[2].IsJailed()) + s.Require().Equal(int64(1000), + providerStakingKeeper.GetLastValidatorPower(s.providerCtx(), vals[2].GetOperator())) + s.Require().Equal(uint64(1), providerKeeper.GetThrottledPacketDataSize(s.providerCtx(), + s.getFirstBundle().Chain.ChainID)) + + // Apply ack on consumer + ackForConsumer = expectedv1Ack + err = consumerKeeper.OnAcknowledgementPacket(s.consumerCtx(), packet2, ackForConsumer) + s.Require().NoError(err) + + // TODO: when provider changes are made, slashRecord.WaitingOnReply should have been updated to false on consumer. Slash Packet will still be in consumer's pending packets queue. + + // Slash record should have been deleted, head of pending packets should have been popped + // Since provider has handled the packet + _, found = consumerKeeper.GetSlashRecord(s.consumerCtx()) + s.Require().False(found) + s.Require().Empty(consumerKeeper.GetPendingPackets(s.consumerCtx())) +} diff --git a/testutil/integration/debug_test.go b/testutil/integration/debug_test.go index 077f33cde3..828c9b6810 100644 --- a/testutil/integration/debug_test.go +++ b/testutil/integration/debug_test.go @@ -252,3 +252,11 @@ func TestQueueAndSendVSCMaturedPackets(t *testing.T) { func TestRecycleTransferChannel(t *testing.T) { runCCVTestByName(t, "TestRecycleTransferChannel") } + +// +// Throttle retry tests +// + +func TestSlashRetries(t *testing.T) { + runCCVTestByName(t, "TestSlashRetries") +} diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index e8b1cb793e..94d5c790fd 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -606,6 +606,17 @@ func (k Keeper) getAndIncrementPendingPacketsIdx(ctx sdk.Context) (toReturn uint return toReturn } +// DeleteHeadOfPendingPackets deletes the head of the pending packets queue. +func (k Keeper) DeleteHeadOfPendingPackets(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte{types.PendingDataPacketsBytePrefix}) + defer iterator.Close() + if !iterator.Valid() { + return + } + store.Delete(iterator.Key()) +} + // GetPendingPackets returns ALL the pending CCV packets from the store without indexes. func (k Keeper) GetPendingPackets(ctx sdk.Context) []ccv.ConsumerPacketData { ppWithIndexes := k.GetAllPendingPacketsWithIdx(ctx) diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index 269c60d9c5..06fdeae082 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -579,3 +579,30 @@ func TestPrevStandaloneChainFlag(t *testing.T) { ck.MarkAsPrevStandaloneChain(ctx) require.True(t, ck.IsPrevStandaloneChain(ctx)) } + +func TestDeleteHeadOfPendingPackets(t *testing.T) { + consumerKeeper, ctx, ctrl, _ := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // append some pending packets + consumerKeeper.AppendPendingPacket(ctx, ccv.VscMaturedPacket, &ccv.ConsumerPacketData_VscMaturedPacketData{}) + consumerKeeper.AppendPendingPacket(ctx, ccv.SlashPacket, &ccv.ConsumerPacketData_SlashPacketData{}) + consumerKeeper.AppendPendingPacket(ctx, ccv.VscMaturedPacket, &ccv.ConsumerPacketData_VscMaturedPacketData{}) + + // Check there's 3 pending packets, vsc matured at head + pp := consumerKeeper.GetPendingPackets(ctx) + require.Len(t, pp, 3) + require.Equal(t, pp[0].Type, ccv.VscMaturedPacket) + + // Delete the head, confirm slash packet is now at head + consumerKeeper.DeleteHeadOfPendingPackets(ctx) + pp = consumerKeeper.GetPendingPackets(ctx) + require.Len(t, pp, 2) + require.Equal(t, pp[0].Type, ccv.SlashPacket) + + // Delete the head, confirm vsc matured packet is now at head + consumerKeeper.DeleteHeadOfPendingPackets(ctx) + pp = consumerKeeper.GetPendingPackets(ctx) + require.Len(t, pp, 1) + require.Equal(t, pp[0].Type, ccv.VscMaturedPacket) +} diff --git a/x/ccv/consumer/keeper/relay.go b/x/ccv/consumer/keeper/relay.go index 376110f26d..060aadff20 100644 --- a/x/ccv/consumer/keeper/relay.go +++ b/x/ccv/consumer/keeper/relay.go @@ -188,8 +188,11 @@ func (k Keeper) SendPackets(ctx sdk.Context) { pending := k.GetAllPendingPacketsWithIdx(ctx) idxsForDeletion := []uint64{} for _, p := range pending { + if !k.PacketSendingPermitted(ctx) { + break + } - // send packet over IBC + // Send packet over IBC err := ccv.SendIBCPacket( ctx, k.scopedKeeper, @@ -213,6 +216,16 @@ func (k Keeper) SendPackets(ctx sdk.Context) { k.Logger(ctx).Error("cannot send IBC packet; leaving packet data stored:", "type", p.Type.String(), "err", err.Error()) break } + // If the packet that was just sent was a Slash packet, set the waiting on slash reply flag. + // This flag will be toggled false again when consumer hears back from provider. See OnAcknowledgementPacket below. + if p.Type == ccv.SlashPacket { + k.UpdateSlashRecordOnSend(ctx) + // Break so slash stays at head of queue. + // This blocks the sending of any other packet until the leading slash packet is handled. + // Also see OnAcknowledgementPacket below which will eventually delete the leading slash packet. + break + } + // Otherwise the vsc matured will be deleted idxsForDeletion = append(idxsForDeletion, p.Idx) } // Delete pending packets that were successfully sent and did not return an error from SendIBCPacket @@ -223,6 +236,40 @@ func (k Keeper) SendPackets(ctx sdk.Context) { // in conjunction with the ibc module's execution of "acknowledgePacket", // according to https://github.com/cosmos/ibc/tree/main/spec/core/ics-004-channel-and-packet-semantics#processing-acknowledgements func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, ack channeltypes.Acknowledgement) error { + if res := ack.GetResult(); res != nil { + if len(res) != 1 { + return fmt.Errorf("acknowledgement result length must be 1, got %d", len(res)) + } + + // Unmarshal into V1 consumer packet data type. We trust data is formed correctly + // as it was originally marshalled by this module, and consumers must trust the provider + // did not tamper with the data. Note ConsumerPacketData.GetBytes() always JSON marshals to the + // ConsumerPacketDataV1 type which is sent over the wire. + var consumerPacket ccv.ConsumerPacketDataV1 + ccv.ModuleCdc.MustUnmarshalJSON(packet.GetData(), &consumerPacket) + // If this ack is regarding a provider handling a vsc matured packet, there's nothing to do. + // As vsc matured packets are popped from the consumer pending packets queue on send. + if consumerPacket.Type == ccv.VscMaturedPacket { + return nil + } + + // Otherwise we handle the result of the slash packet acknowledgement. + switch res[0] { + // We treat a v1 result as the provider successfully queuing the slash packet w/o need for retry. + case ccv.V1Result[0]: + k.ClearSlashRecord(ctx) // Clears slash record state, unblocks sending of pending packets. + k.DeleteHeadOfPendingPackets(ctx) // Remove slash from head of queue. It's been handled. + case ccv.SlashPacketHandledResult[0]: + k.ClearSlashRecord(ctx) // Clears slash record state, unblocks sending of pending packets. + k.DeleteHeadOfPendingPackets(ctx) // Remove slash from head of queue. It's been handled. + case ccv.SlashPacketBouncedResult[0]: + k.UpdateSlashRecordOnBounce(ctx) + // Note slash is still at head of queue and will now be retried after appropriate delay period. + default: + return fmt.Errorf("unrecognized acknowledgement result: %c", res[0]) + } + } + if err := ack.GetError(); err != "" { // Reasons for ErrorAcknowledgment // - packet data could not be successfully decoded diff --git a/x/ccv/consumer/keeper/relay_test.go b/x/ccv/consumer/keeper/relay_test.go index ed6771900e..09cf987fc0 100644 --- a/x/ccv/consumer/keeper/relay_test.go +++ b/x/ccv/consumer/keeper/relay_test.go @@ -14,6 +14,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -22,6 +23,7 @@ import ( "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v3/x/ccv/types" ) @@ -210,10 +212,105 @@ func TestOnRecvVSCPacketDuplicateUpdates(t *testing.T) { require.Equal(t, valUpdates[1], gotPendingChanges.ValidatorUpdates[0]) // Only latest update should be kept } -// TestOnAcknowledgementPacket tests application logic for acknowledgments of sent VSCMatured and Slash packets +// TestSendPackets tests the SendPackets method failing +func TestSendPacketsFailure(t *testing.T) { + // Keeper setup + consumerKeeper, ctx, ctrl, mocks := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + consumerKeeper.SetProviderChannel(ctx, "consumerCCVChannelID") + consumerKeeper.SetParams(ctx, consumertypes.DefaultParams()) + + // Set some pending packets + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{}) + consumerKeeper.AppendPendingPacket(ctx, types.SlashPacket, &types.ConsumerPacketData_SlashPacketData{}) + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{}) + + // Mock the channel keeper to return an error + gomock.InOrder( + mocks.MockChannelKeeper.EXPECT().GetChannel(ctx, types.ConsumerPortID, + "consumerCCVChannelID").Return(channeltypes.Channel{}, false).Times(1), + ) + + // No panic should occur, pending packets should not be cleared + consumerKeeper.SendPackets(ctx) + require.Equal(t, 3, len(consumerKeeper.GetPendingPackets(ctx))) +} + +func TestSendPackets(t *testing.T) { + // Keeper setup + consumerKeeper, ctx, ctrl, mocks := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + consumerKeeper.SetProviderChannel(ctx, "consumerCCVChannelID") + consumerKeeper.SetParams(ctx, consumertypes.DefaultParams()) + + // No slash record should exist + _, found := consumerKeeper.GetSlashRecord(ctx) + require.False(t, found) + require.True(t, consumerKeeper.PacketSendingPermitted(ctx)) + + // Queue up two vsc matured, followed by slash, followed by vsc matured + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: &types.VSCMaturedPacketData{ + ValsetUpdateId: 77, + }, + }) + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: &types.VSCMaturedPacketData{ + ValsetUpdateId: 90, + }, + }) + consumerKeeper.AppendPendingPacket(ctx, types.SlashPacket, &types.ConsumerPacketData_SlashPacketData{ + SlashPacketData: &types.SlashPacketData{ + Validator: abci.Validator{}, + ValsetUpdateId: 88, + Infraction: stakingtypes.Infraction_INFRACTION_DOWNTIME, + }, + }) + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: &types.VSCMaturedPacketData{ + ValsetUpdateId: 99, + }, + }) + + // First two vsc matured and slash should be sent, 3 total + gomock.InAnyOrder( + testkeeper.GetMocksForSendIBCPacket(ctx, mocks, "consumerCCVChannelID", 3), + ) + consumerKeeper.SendPackets(ctx) + ctrl.Finish() + + // First two packets should be deleted, slash should be at head of queue + pendingPackets := consumerKeeper.GetPendingPackets(ctx) + require.Equal(t, 2, len(pendingPackets)) + require.Equal(t, types.SlashPacket, pendingPackets[0].Type) + require.Equal(t, types.VscMaturedPacket, pendingPackets[1].Type) + + // Packet sending not permitted + require.False(t, consumerKeeper.PacketSendingPermitted(ctx)) + + // Now delete slash record as would be done by a recv SlashPacketHandledResult + // then confirm last vsc matured is sent + consumerKeeper.ClearSlashRecord(ctx) + consumerKeeper.DeleteHeadOfPendingPackets(ctx) + + // Packet sending permitted + require.True(t, consumerKeeper.PacketSendingPermitted(ctx)) + + gomock.InAnyOrder( + testkeeper.GetMocksForSendIBCPacket(ctx, mocks, "consumerCCVChannelID", 1), + ) + + consumerKeeper.SendPackets(ctx) + ctrl.Finish() + + // No packets should be left + pendingPackets = consumerKeeper.GetPendingPackets(ctx) + require.Equal(t, 0, len(pendingPackets)) +} + +// TestOnAcknowledgementPacketError tests application logic for ERROR acknowledgments of sent VSCMatured and Slash packets // in conjunction with the ibc module's execution of "acknowledgePacket", // according to https://github.com/cosmos/ibc/tree/main/spec/core/ics-004-channel-and-packet-semantics#processing-acknowledgements -func TestOnAcknowledgementPacket(t *testing.T) { +func TestOnAcknowledgementPacketError(t *testing.T) { // Channel ID to some dest chain that's not the established provider channelIDToDestChain := "channelIDToDestChain" @@ -258,12 +355,6 @@ func TestOnAcknowledgementPacket(t *testing.T) { uint64(time.Now().Add(60*time.Second).UnixNano()), ) - ack := channeltypes.NewResultAcknowledgement([]byte{1}) - - // expect no error returned from OnAcknowledgementPacket, no input error with ack - err := consumerKeeper.OnAcknowledgementPacket(ctx, packet, ack) - require.Nil(t, err) - // Still expect no error returned from OnAcknowledgementPacket, // but the input error ack will be handled with appropriate ChanCloseInit calls dummyCap := &capabilitytypes.Capability{} @@ -287,33 +378,91 @@ func TestOnAcknowledgementPacket(t *testing.T) { ).Return(nil).Times(1), ) - ack = types.NewErrorAcknowledgementWithLog(ctx, fmt.Errorf("error")) - err = consumerKeeper.OnAcknowledgementPacket(ctx, packet, ack) + ack := types.NewErrorAcknowledgementWithLog(ctx, fmt.Errorf("error")) + err := consumerKeeper.OnAcknowledgementPacket(ctx, packet, ack) require.Nil(t, err) } -// TestSendPackets tests the SendPackets method failing -func TestSendPacketsFailure(t *testing.T) { - // Keeper setup - consumerKeeper, ctx, ctrl, mocks := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) +// TestOnAcknowledgementPacketResult tests application logic for RESULT acknowledgments of sent VSCMatured and Slash packets +// in conjunction with the ibc module's execution of "acknowledgePacket", +func TestOnAcknowledgementPacketResult(t *testing.T) { + // Setup + consumerKeeper, ctx, ctrl, _ := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - consumerKeeper.SetProviderChannel(ctx, "consumerCCVChannelID") - consumerKeeper.SetParams(ctx, consumertypes.DefaultParams()) - // Set some pending packets - consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{}) - consumerKeeper.AppendPendingPacket(ctx, types.SlashPacket, &types.ConsumerPacketData_SlashPacketData{}) - consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{}) + setupSlashBeforeVscMatured(ctx, &consumerKeeper) - // Mock the channel keeper to return an error - gomock.InOrder( - mocks.MockChannelKeeper.EXPECT().GetChannel(ctx, types.ConsumerPortID, - "consumerCCVChannelID").Return(channeltypes.Channel{}, false).Times(1), - ) + // Slash record found, 2 pending packets, slash is at head of queue + _, found := consumerKeeper.GetSlashRecord(ctx) + require.True(t, found) + pendingPackets := consumerKeeper.GetPendingPackets(ctx) + require.Len(t, pendingPackets, 2) + require.Equal(t, types.SlashPacket, pendingPackets[0].Type) - // No panic should occur, pending packets should not be cleared - consumerKeeper.SendPackets(ctx) - require.Equal(t, 3, len(consumerKeeper.GetPendingPackets(ctx))) + // v1 result should delete slash record and head of pending packets. Vsc matured remains + ack := channeltypes.NewResultAcknowledgement(types.V1Result) + packet := channeltypes.Packet{Data: pendingPackets[0].GetBytes()} + err := consumerKeeper.OnAcknowledgementPacket(ctx, packet, ack) + require.Nil(t, err) + _, found = consumerKeeper.GetSlashRecord(ctx) + require.False(t, found) + require.Len(t, consumerKeeper.GetPendingPackets(ctx), 1) + require.Equal(t, types.VscMaturedPacket, consumerKeeper.GetPendingPackets(ctx)[0].Type) + + // refresh state + setupSlashBeforeVscMatured(ctx, &consumerKeeper) + pendingPackets = consumerKeeper.GetPendingPackets(ctx) + packet = channeltypes.Packet{Data: pendingPackets[0].GetBytes()} + + // Slash packet handled result should delete slash record and head of pending packets + ack = channeltypes.NewResultAcknowledgement(types.SlashPacketHandledResult) + err = consumerKeeper.OnAcknowledgementPacket(ctx, packet, ack) + require.Nil(t, err) + _, found = consumerKeeper.GetSlashRecord(ctx) + require.False(t, found) + require.Len(t, consumerKeeper.GetPendingPackets(ctx), 1) + require.Equal(t, types.VscMaturedPacket, consumerKeeper.GetPendingPackets(ctx)[0].Type) + + // refresh state + setupSlashBeforeVscMatured(ctx, &consumerKeeper) + pendingPackets = consumerKeeper.GetPendingPackets(ctx) + packet = channeltypes.Packet{Data: pendingPackets[0].GetBytes()} + + slashRecordBefore, found := consumerKeeper.GetSlashRecord(ctx) + require.True(t, found) + require.True(t, slashRecordBefore.WaitingOnReply) + + // Slash packet bounced result should update slash record + ack = channeltypes.NewResultAcknowledgement(types.SlashPacketBouncedResult) + err = consumerKeeper.OnAcknowledgementPacket(ctx, packet, ack) + require.Nil(t, err) + slashRecordAfter, found := consumerKeeper.GetSlashRecord(ctx) + require.True(t, found) + require.False(t, slashRecordAfter.WaitingOnReply) // waiting on reply toggled false + require.Equal(t, slashRecordAfter.SendTime.UnixNano(), + slashRecordBefore.SendTime.UnixNano()) // send time NOT updated. Bounce result shouldn't affect that +} + +func setupSlashBeforeVscMatured(ctx sdk.Context, k *consumerkeeper.Keeper) { + // clear old state + k.ClearSlashRecord(ctx) + k.DeleteAllPendingDataPackets(ctx) + + // Set some related state to test against + k.SetSlashRecord(ctx, consumertypes.SlashRecord{WaitingOnReply: true, SendTime: time.Now()}) + // Slash packet before VSCMatured packet + k.AppendPendingPacket(ctx, types.SlashPacket, &types.ConsumerPacketData_SlashPacketData{ // Slash appears first + SlashPacketData: &types.SlashPacketData{ + Validator: abci.Validator{}, + ValsetUpdateId: 88, + Infraction: stakingtypes.Infraction_INFRACTION_DOWNTIME, + }, + }) + k.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: &types.VSCMaturedPacketData{ + ValsetUpdateId: 90, + }, + }) } // Regression test for https://github.com/cosmos/interchain-security/issues/1145 @@ -324,7 +473,12 @@ func TestSendPacketsDeletion(t *testing.T) { consumerKeeper.SetProviderChannel(ctx, "consumerCCVChannelID") consumerKeeper.SetParams(ctx, consumertypes.DefaultParams()) - // Queue two pending packets + // Queue two pending packets, vsc matured first + consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{ + VscMaturedPacketData: &types.VSCMaturedPacketData{ + ValsetUpdateId: 90, + }, + }) consumerKeeper.AppendPendingPacket(ctx, types.SlashPacket, &types.ConsumerPacketData_SlashPacketData{ // Slash appears first SlashPacketData: &types.SlashPacketData{ Validator: abci.Validator{}, @@ -332,15 +486,10 @@ func TestSendPacketsDeletion(t *testing.T) { Infraction: stakingtypes.Infraction_INFRACTION_DOWNTIME, }, }) - consumerKeeper.AppendPendingPacket(ctx, types.VscMaturedPacket, &types.ConsumerPacketData_VscMaturedPacketData{ - VscMaturedPacketData: &types.VSCMaturedPacketData{ - ValsetUpdateId: 90, - }, - }) - // Get mocks for a successful SendPacket call that does NOT return an error + // Get mocks for the (first) successful SendPacket call that does NOT return an error expectations := testkeeper.GetMocksForSendIBCPacket(ctx, mocks, "consumerCCVChannelID", 1) - // Append mocks for a failed SendPacket call, which returns an error + // Append mocks for the (second) failed SendPacket call, which returns an error expectations = append(expectations, mocks.MockChannelKeeper.EXPECT().GetChannel(ctx, types.ConsumerPortID, "consumerCCVChannelID").Return(channeltypes.Channel{}, false).Times(1)) gomock.InOrder(expectations...) @@ -349,5 +498,7 @@ func TestSendPacketsDeletion(t *testing.T) { // Expect the first successfully sent packet to be popped from queue require.Equal(t, 1, len(consumerKeeper.GetPendingPackets(ctx))) - require.Equal(t, types.VscMaturedPacket, consumerKeeper.GetPendingPackets(ctx)[0].Type) + + // Expect the slash packet to remain + require.Equal(t, types.SlashPacket, consumerKeeper.GetPendingPackets(ctx)[0].Type) } diff --git a/x/ccv/consumer/keeper/throttle_retry.go b/x/ccv/consumer/keeper/throttle_retry.go new file mode 100644 index 0000000000..4c4585cb1d --- /dev/null +++ b/x/ccv/consumer/keeper/throttle_retry.go @@ -0,0 +1,112 @@ +package keeper + +import ( + "fmt" + "time" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + + consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" +) + +// +// Throttling with retries follows a finite-state machine design: +// +// 2 states: "No Slash" and "Standby". +// Initial State: "No Slash" +// Transition Event: ("No Slash", Slash packet sent) => ("Standby") +// Transition Event: ("Standby", V1Result ack received) => ("No Slash") +// Transition Event: ("Standby", Slash packet successfully handled) => ("No Slash") +// Internal Transition Event: ("Standby", Slash packet bounced) => ("Standby", with SlashRecord.WaitingOnReply = false) +// Transition Event: ("Standby", Retry sent) => ("Standby", new cycle) +// +// Description in words: +// +// 1. "No slash": If no slash record exists, the consumer is permitted to send packets from the pending packets queue. +// The consumer starts in this state from genesis. +// +// 2. On the event that a slash packet is obtained from the head of the pending packets queue and sent, +// a consumer transitions from "No Slash" to "Standby". A slash record is created upon entry to this state, +// and the consumer is now restricted from sending anymore packets. +// +// The slash packet remains at the head of the pending packets queue within the "Standby" state. +// +// - If the consumer receives a V1Result ack from the provider, +// OR if the consumer receives an ack from the provider that the slash packet was successfully handled, +// the consumer transitions from "Standby" to "No Slash". +// The slash record is cleared upon this transition, and the slash packet is popped from the pending packets queue. +// +// - Else if the consumer receives an ack from the provider that the slash packet was bounced (not handled), +// then SlashRecord.WaitingOnReply is set false, and the consumer retries sending the slash packet after a delay period. +// +// Once a retry is sent, the consumer enters a new cycle of the "Standby" state and the process repeats. +// +// This design is implemented below, and in relay.go under SendPackets() and OnAcknowledgementPacket(). +// + +// Retry delay period could be implemented as a param, but 1 hour is reasonable +const RetryDelayPeriod = time.Hour + +// PacketSendingPermitted returns whether the consumer is allowed to send packets +// from the pending packets queue. +func (k Keeper) PacketSendingPermitted(ctx sdktypes.Context) bool { + record, found := k.GetSlashRecord(ctx) + if !found { + // no slash record exists, send is permitted + return true + } + if record.WaitingOnReply { + // We are waiting on a reply from provider, block sending + return false + } + // If retry delay period has elapsed, we can send again + return ctx.BlockTime().After(record.SendTime.Add(RetryDelayPeriod)) +} + +func (k Keeper) UpdateSlashRecordOnSend(ctx sdktypes.Context) { + record := consumertypes.NewSlashRecord( + ctx.BlockTime(), // sendTime + true, // waitingOnReply + ) + // We don't mind overwriting here, since this is either a retry or the first time we send a slash + k.SetSlashRecord(ctx, record) +} + +func (k Keeper) UpdateSlashRecordOnBounce(ctx sdktypes.Context) { + record, found := k.GetSlashRecord(ctx) + if !found { + // This should never happen + panic("could not find slash record, but reply was received from provider") + } + record.WaitingOnReply = false + k.SetSlashRecord(ctx, record) +} + +func (k Keeper) GetSlashRecord(ctx sdktypes.Context) (record consumertypes.SlashRecord, found bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(consumertypes.SlashRecordKey()) + if bz == nil { + return record, false + } + err := record.Unmarshal(bz) + if err != nil { + // This should never happen + panic(fmt.Sprintf("could not unmarshal slash record: %v", err)) + } + return record, true +} + +func (k Keeper) SetSlashRecord(ctx sdktypes.Context, record consumertypes.SlashRecord) { + store := ctx.KVStore(k.storeKey) + bz, err := record.Marshal() + if err != nil { + // This should never happen + panic(fmt.Sprintf("could not marshal slash record: %v", err)) + } + store.Set(consumertypes.SlashRecordKey(), bz) +} + +func (k Keeper) ClearSlashRecord(ctx sdktypes.Context) { + store := ctx.KVStore(k.storeKey) + store.Delete(consumertypes.SlashRecordKey()) +} diff --git a/x/ccv/consumer/keeper/throttle_retry_test.go b/x/ccv/consumer/keeper/throttle_retry_test.go new file mode 100644 index 0000000000..cc14ce3cdd --- /dev/null +++ b/x/ccv/consumer/keeper/throttle_retry_test.go @@ -0,0 +1,91 @@ +package keeper_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + testutil "github.com/cosmos/interchain-security/v3/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" +) + +func TestPacketSendingPermitted(t *testing.T) { + consumerKeeper, ctx, ctrl, _ := testutil.GetConsumerKeeperAndCtx(t, testutil.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + ctx = ctx.WithBlockTime(time.Now()) + + // No slash record exists, send is permitted + slashRecord, found := consumerKeeper.GetSlashRecord(ctx) + require.False(t, found) + require.Zero(t, slashRecord) + require.True(t, consumerKeeper.PacketSendingPermitted(ctx)) + + // Update slash record on sending of slash packet + consumerKeeper.UpdateSlashRecordOnSend(ctx) + slashRecord, found = consumerKeeper.GetSlashRecord(ctx) + require.True(t, found) + require.True(t, slashRecord.WaitingOnReply) + + // Packet sending not permitted since we're waiting on a reply from provider + require.False(t, consumerKeeper.PacketSendingPermitted(ctx)) + + // Call update that happens when provider bounces slash packet + consumerKeeper.UpdateSlashRecordOnBounce(ctx) + slashRecord, found = consumerKeeper.GetSlashRecord(ctx) + require.True(t, found) + require.False(t, slashRecord.WaitingOnReply) + + // Packet sending still not permitted since retry delay period has not elapsed + require.False(t, consumerKeeper.PacketSendingPermitted(ctx)) + + // Elapse retry delay period + ctx = ctx.WithBlockTime(ctx.BlockTime().Add(2 * consumerkeeper.RetryDelayPeriod)) + + // Now packet sending is permitted again + require.True(t, consumerKeeper.PacketSendingPermitted(ctx)) +} + +func TestThrottleRetryCRUD(t *testing.T) { + consumerKeeper, ctx, ctrl, _ := testutil.GetConsumerKeeperAndCtx(t, testutil.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + slashRecord, found := consumerKeeper.GetSlashRecord(ctx) + require.False(t, found) + require.Zero(t, slashRecord) + + consumerKeeper.SetSlashRecord(ctx, consumertypes.SlashRecord{ + WaitingOnReply: true, + SendTime: ctx.BlockTime(), + }) + + slashRecord, found = consumerKeeper.GetSlashRecord(ctx) + require.True(t, found) + require.True(t, slashRecord.WaitingOnReply) + require.Equal(t, ctx.BlockTime(), slashRecord.SendTime) + + // UpdateSlashRecordOnBounce should set WaitingOnReply to false, and leave SendTime unchanged + oldBlocktime := ctx.BlockTime() + ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Hour)) + consumerKeeper.UpdateSlashRecordOnBounce(ctx) + slashRecord, found = consumerKeeper.GetSlashRecord(ctx) + require.True(t, found) + require.False(t, slashRecord.WaitingOnReply) + require.Equal(t, oldBlocktime, slashRecord.SendTime) // Old SendTime expected + + // UpdateSlashRecordOnSend should replace slash record with WaitingOnReply set to true, and new SendTime + ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Hour)) + consumerKeeper.UpdateSlashRecordOnSend(ctx) + slashRecord, found = consumerKeeper.GetSlashRecord(ctx) + require.True(t, found) + require.True(t, slashRecord.WaitingOnReply) + require.Equal(t, ctx.BlockTime(), slashRecord.SendTime) // New SendTime expected + require.Equal(t, oldBlocktime.Add(2*time.Hour), slashRecord.SendTime) // Sanity check + + consumerKeeper.ClearSlashRecord(ctx) + slashRecord, found = consumerKeeper.GetSlashRecord(ctx) + require.False(t, found) + require.Zero(t, slashRecord) +} diff --git a/x/ccv/consumer/types/consumer.pb.go b/x/ccv/consumer/types/consumer.pb.go index 90d5d6e12b..b16b561b7b 100644 --- a/x/ccv/consumer/types/consumer.pb.go +++ b/x/ccv/consumer/types/consumer.pb.go @@ -354,11 +354,66 @@ func (m *MaturingVSCPacket) GetMaturityTime() time.Time { return time.Time{} } +// A record storing the state of a slash packet sent to the provider chain +// which may bounce back and forth until handled by the provider. +type SlashRecord struct { + WaitingOnReply bool `protobuf:"varint,1,opt,name=waiting_on_reply,json=waitingOnReply,proto3" json:"waiting_on_reply,omitempty"` + SendTime time.Time `protobuf:"bytes,2,opt,name=send_time,json=sendTime,proto3,stdtime" json:"send_time"` +} + +func (m *SlashRecord) Reset() { *m = SlashRecord{} } +func (m *SlashRecord) String() string { return proto.CompactTextString(m) } +func (*SlashRecord) ProtoMessage() {} +func (*SlashRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_5b27a82b276e7f93, []int{4} +} +func (m *SlashRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SlashRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SlashRecord.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SlashRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_SlashRecord.Merge(m, src) +} +func (m *SlashRecord) XXX_Size() int { + return m.Size() +} +func (m *SlashRecord) XXX_DiscardUnknown() { + xxx_messageInfo_SlashRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_SlashRecord proto.InternalMessageInfo + +func (m *SlashRecord) GetWaitingOnReply() bool { + if m != nil { + return m.WaitingOnReply + } + return false +} + +func (m *SlashRecord) GetSendTime() time.Time { + if m != nil { + return m.SendTime + } + return time.Time{} +} + func init() { proto.RegisterType((*Params)(nil), "interchain_security.ccv.consumer.v1.Params") proto.RegisterType((*LastTransmissionBlockHeight)(nil), "interchain_security.ccv.consumer.v1.LastTransmissionBlockHeight") proto.RegisterType((*CrossChainValidator)(nil), "interchain_security.ccv.consumer.v1.CrossChainValidator") proto.RegisterType((*MaturingVSCPacket)(nil), "interchain_security.ccv.consumer.v1.MaturingVSCPacket") + proto.RegisterType((*SlashRecord)(nil), "interchain_security.ccv.consumer.v1.SlashRecord") } func init() { @@ -366,57 +421,60 @@ func init() { } var fileDescriptor_5b27a82b276e7f93 = []byte{ - // 786 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xcf, 0x6e, 0xdb, 0x36, - 0x18, 0x8f, 0x96, 0xd6, 0x4d, 0x98, 0x14, 0x6b, 0x59, 0x2f, 0x55, 0x33, 0x40, 0x76, 0xdd, 0x1e, - 0x7c, 0x89, 0x84, 0x26, 0xdb, 0xa5, 0xc0, 0x0e, 0xb5, 0xb3, 0xa2, 0xdd, 0xbf, 0x78, 0xaa, 0xd1, - 0x01, 0xdb, 0x81, 0xa0, 0x28, 0x5a, 0x22, 0x22, 0x91, 0x02, 0x49, 0xa9, 0xd3, 0x7d, 0x0f, 0xd0, - 0xe3, 0x1e, 0x61, 0x0f, 0xb0, 0x87, 0x28, 0x76, 0xea, 0x71, 0xa7, 0x6e, 0x48, 0xde, 0x60, 0x4f, - 0x30, 0x90, 0x92, 0x5c, 0x3b, 0x6d, 0x80, 0xdc, 0xf8, 0xe9, 0xf7, 0xfb, 0x7e, 0xfa, 0xfe, 0x83, - 0x43, 0xc6, 0x35, 0x95, 0x24, 0xc5, 0x8c, 0x23, 0x45, 0x49, 0x29, 0x99, 0xae, 0x03, 0x42, 0xaa, - 0x80, 0x08, 0xae, 0xca, 0x9c, 0xca, 0xa0, 0x7a, 0xb4, 0x7c, 0xfb, 0x85, 0x14, 0x5a, 0xc0, 0x07, - 0x1f, 0xf1, 0xf1, 0x09, 0xa9, 0xfc, 0x25, 0xaf, 0x7a, 0xb4, 0xff, 0xf0, 0x32, 0x61, 0xa3, 0x47, - 0xaa, 0x46, 0x6a, 0xff, 0x5e, 0x22, 0x44, 0x92, 0xd1, 0xc0, 0x5a, 0x51, 0xb9, 0x08, 0x30, 0xaf, - 0x5b, 0xa8, 0x9f, 0x88, 0x44, 0xd8, 0x67, 0x60, 0x5e, 0x9d, 0x03, 0x11, 0x2a, 0x17, 0x0a, 0x35, - 0x40, 0x63, 0xb4, 0x90, 0x77, 0x51, 0x2b, 0x2e, 0x25, 0xd6, 0x4c, 0xf0, 0x16, 0x1f, 0x5c, 0xc4, - 0x35, 0xcb, 0xa9, 0xd2, 0x38, 0x2f, 0x1a, 0xc2, 0xe8, 0xb7, 0x1e, 0xe8, 0xcd, 0xb0, 0xc4, 0xb9, - 0x82, 0x2e, 0xb8, 0x41, 0x39, 0x8e, 0x32, 0x1a, 0xbb, 0xce, 0xd0, 0x19, 0x6f, 0x85, 0x9d, 0x09, - 0x4f, 0xc0, 0xc3, 0x28, 0x13, 0xe4, 0x54, 0xa1, 0x82, 0x4a, 0x14, 0x33, 0xa5, 0x25, 0x8b, 0x4a, - 0xf3, 0x1b, 0xa4, 0x25, 0xe6, 0x2a, 0x67, 0x4a, 0x31, 0xc1, 0xdd, 0x4f, 0x86, 0xce, 0x78, 0x33, - 0xbc, 0xdf, 0x70, 0x67, 0x54, 0x1e, 0xaf, 0x30, 0xe7, 0x2b, 0x44, 0xf8, 0x0d, 0xb8, 0x7f, 0xa9, - 0x0a, 0x22, 0x29, 0xe6, 0x9c, 0x66, 0xee, 0xe6, 0xd0, 0x19, 0x6f, 0x87, 0x83, 0xf8, 0x12, 0x91, - 0x69, 0x43, 0x83, 0x8f, 0xc1, 0x7e, 0x21, 0x45, 0xc5, 0x62, 0x2a, 0xd1, 0x82, 0x52, 0x54, 0x08, - 0x91, 0x21, 0x1c, 0xc7, 0x12, 0x29, 0x2d, 0xdd, 0x6b, 0x56, 0x64, 0xaf, 0x63, 0x3c, 0xa5, 0x74, - 0x26, 0x44, 0xf6, 0x24, 0x8e, 0xe5, 0x0b, 0x2d, 0xe1, 0x8f, 0x00, 0x12, 0x52, 0x21, 0x53, 0x14, - 0x51, 0x6a, 0x93, 0x1d, 0x13, 0xb1, 0x7b, 0x7d, 0xe8, 0x8c, 0x77, 0x0e, 0xef, 0xf9, 0x4d, 0xed, - 0xfc, 0xae, 0x76, 0xfe, 0x71, 0x5b, 0xdb, 0xc9, 0xd6, 0x9b, 0x77, 0x83, 0x8d, 0xdf, 0xff, 0x19, - 0x38, 0xe1, 0x2d, 0x42, 0xaa, 0x79, 0xe3, 0x3d, 0xb3, 0xce, 0xf0, 0x17, 0x70, 0xd7, 0x66, 0xb3, - 0xa0, 0xf2, 0xa2, 0x6e, 0xef, 0xea, 0xba, 0x9f, 0x75, 0x1a, 0xeb, 0xe2, 0xcf, 0xc0, 0xb0, 0x9b, - 0x37, 0x24, 0xe9, 0x5a, 0x09, 0x17, 0x12, 0x13, 0xf3, 0x70, 0x6f, 0xd8, 0x8c, 0xbd, 0x8e, 0x17, - 0xae, 0xd1, 0x9e, 0xb6, 0x2c, 0x78, 0x00, 0x60, 0xca, 0x94, 0x16, 0x92, 0x11, 0x9c, 0x21, 0xca, - 0xb5, 0x64, 0x54, 0xb9, 0x5b, 0xb6, 0x81, 0xb7, 0xdf, 0x23, 0x5f, 0x37, 0x00, 0xfc, 0x01, 0xdc, - 0x2a, 0x79, 0x24, 0x78, 0xcc, 0x78, 0xd2, 0xa5, 0xb3, 0x7d, 0xf5, 0x74, 0x3e, 0x5d, 0x3a, 0xb7, - 0x89, 0x1c, 0x81, 0x3d, 0x25, 0x16, 0x1a, 0x89, 0x42, 0x23, 0x53, 0x21, 0x9d, 0x4a, 0xaa, 0x52, - 0x91, 0xc5, 0x2e, 0xb0, 0xe1, 0xdf, 0x31, 0xe8, 0x49, 0xa1, 0x4f, 0x4a, 0x3d, 0xef, 0x20, 0xf8, - 0x00, 0xdc, 0x94, 0xf4, 0x15, 0x96, 0x31, 0x8a, 0x29, 0x17, 0xb9, 0x72, 0x77, 0x86, 0x9b, 0xe3, - 0xed, 0x70, 0xb7, 0xf9, 0x78, 0x6c, 0xbf, 0xc1, 0x2f, 0xc0, 0xb2, 0xd9, 0x68, 0x9d, 0xbd, 0x6b, - 0xd9, 0xfd, 0x0e, 0x0d, 0x57, 0xbc, 0x46, 0x5f, 0x82, 0xcf, 0xbf, 0xc3, 0x4a, 0xaf, 0xce, 0xd7, - 0xc4, 0x4c, 0xf1, 0x33, 0xca, 0x92, 0x54, 0xc3, 0x3d, 0xd0, 0x4b, 0xed, 0xcb, 0x6e, 0xc6, 0x66, - 0xd8, 0x5a, 0xa3, 0x3f, 0x1c, 0x70, 0x67, 0x2a, 0x85, 0x52, 0x53, 0xb3, 0xf3, 0x2f, 0x71, 0xc6, - 0x62, 0xac, 0x85, 0x34, 0xab, 0x64, 0x26, 0x90, 0x2a, 0x65, 0x1d, 0x76, 0xc3, 0xce, 0x84, 0x7d, - 0x70, 0xbd, 0x10, 0xaf, 0xa8, 0x6c, 0x77, 0xa5, 0x31, 0x20, 0x06, 0xbd, 0xa2, 0x8c, 0x4e, 0x69, - 0x6d, 0x87, 0x7e, 0xe7, 0xb0, 0xff, 0x41, 0x51, 0x9f, 0xf0, 0x7a, 0x72, 0xf4, 0xdf, 0xbb, 0xc1, - 0xdd, 0x1a, 0xe7, 0xd9, 0xe3, 0x91, 0xe9, 0x2e, 0xe5, 0xaa, 0x54, 0xa8, 0xf1, 0x1b, 0xfd, 0xf5, - 0xe7, 0x41, 0xbf, 0xbd, 0x0c, 0x44, 0xd6, 0x85, 0x16, 0xfe, 0xac, 0x8c, 0xbe, 0xa5, 0x75, 0xd8, - 0x0a, 0x8f, 0x34, 0xb8, 0xfd, 0x3d, 0xd6, 0xa5, 0x64, 0x3c, 0x79, 0xf9, 0x62, 0x3a, 0xc3, 0xe4, - 0x94, 0x6a, 0x13, 0x4d, 0xa5, 0xc8, 0xf3, 0x66, 0xe1, 0xaf, 0x85, 0x8d, 0x01, 0x9f, 0x83, 0x9b, - 0xb9, 0xa5, 0xea, 0xda, 0x8e, 0xb0, 0x8d, 0x75, 0xe7, 0x70, 0xff, 0x83, 0xa0, 0xe6, 0xdd, 0x31, - 0x69, 0x5a, 0xfd, 0xda, 0xb4, 0x7a, 0xb7, 0x73, 0x35, 0xe0, 0xe4, 0xa7, 0x37, 0x67, 0x9e, 0xf3, - 0xf6, 0xcc, 0x73, 0xfe, 0x3d, 0xf3, 0x9c, 0xd7, 0xe7, 0xde, 0xc6, 0xdb, 0x73, 0x6f, 0xe3, 0xef, - 0x73, 0x6f, 0xe3, 0xe7, 0xaf, 0x12, 0xa6, 0xd3, 0x32, 0xf2, 0x89, 0xc8, 0xdb, 0x93, 0x16, 0xbc, - 0xbf, 0x9e, 0x07, 0xcb, 0xeb, 0x59, 0x1d, 0x05, 0xbf, 0xae, 0xdf, 0x66, 0x5d, 0x17, 0x54, 0x45, - 0x3d, 0x1b, 0xc4, 0xd1, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x26, 0xe0, 0xb8, 0xdf, 0xcc, 0x05, - 0x00, 0x00, + // 836 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xcf, 0x6e, 0xdb, 0x36, + 0x18, 0x8f, 0x96, 0xd6, 0x4d, 0xe8, 0x74, 0x4b, 0x59, 0x2f, 0x55, 0x33, 0xc0, 0x76, 0xdd, 0x1e, + 0x7c, 0x89, 0x8d, 0x26, 0xdb, 0xa5, 0xc0, 0x0e, 0xf9, 0xb3, 0xa2, 0xdd, 0xbf, 0x78, 0x4a, 0xd0, + 0x01, 0xdb, 0x81, 0xa0, 0xa8, 0xcf, 0x16, 0x11, 0x89, 0x14, 0x48, 0x4a, 0x99, 0x76, 0xde, 0x03, + 0xf4, 0xb8, 0x47, 0xd8, 0x03, 0xec, 0x21, 0x8a, 0x9d, 0x7a, 0xdc, 0xa9, 0x1b, 0x92, 0x37, 0xd8, + 0x13, 0x0c, 0xa4, 0x24, 0xd7, 0x4e, 0x17, 0xa0, 0xbb, 0xf1, 0xe3, 0xef, 0x8f, 0xf8, 0x7d, 0xfc, + 0xf8, 0x09, 0xed, 0x72, 0x61, 0x40, 0xb1, 0x98, 0x72, 0x41, 0x34, 0xb0, 0x5c, 0x71, 0x53, 0x8e, + 0x19, 0x2b, 0xc6, 0x4c, 0x0a, 0x9d, 0xa7, 0xa0, 0xc6, 0xc5, 0xe3, 0xf9, 0x7a, 0x94, 0x29, 0x69, + 0x24, 0x7e, 0xf8, 0x1f, 0x9a, 0x11, 0x63, 0xc5, 0x68, 0xce, 0x2b, 0x1e, 0x6f, 0x3f, 0xba, 0xce, + 0xd8, 0xfa, 0xb1, 0xa2, 0xb2, 0xda, 0xbe, 0x3f, 0x93, 0x72, 0x96, 0xc0, 0xd8, 0x45, 0x61, 0x3e, + 0x1d, 0x53, 0x51, 0xd6, 0x50, 0x67, 0x26, 0x67, 0xd2, 0x2d, 0xc7, 0x76, 0xd5, 0x08, 0x98, 0xd4, + 0xa9, 0xd4, 0xa4, 0x02, 0xaa, 0xa0, 0x86, 0xba, 0x57, 0xbd, 0xa2, 0x5c, 0x51, 0xc3, 0xa5, 0xa8, + 0xf1, 0xde, 0x55, 0xdc, 0xf0, 0x14, 0xb4, 0xa1, 0x69, 0x56, 0x11, 0x06, 0xbf, 0xb4, 0x50, 0x6b, + 0x42, 0x15, 0x4d, 0x35, 0xf6, 0xd1, 0x2d, 0x10, 0x34, 0x4c, 0x20, 0xf2, 0xbd, 0xbe, 0x37, 0x5c, + 0x0b, 0x9a, 0x10, 0x1f, 0xa3, 0x47, 0x61, 0x22, 0xd9, 0x99, 0x26, 0x19, 0x28, 0x12, 0x71, 0x6d, + 0x14, 0x0f, 0x73, 0xfb, 0x19, 0x62, 0x14, 0x15, 0x3a, 0xe5, 0x5a, 0x73, 0x29, 0xfc, 0x0f, 0xfa, + 0xde, 0x70, 0x35, 0x78, 0x50, 0x71, 0x27, 0xa0, 0x8e, 0x16, 0x98, 0xa7, 0x0b, 0x44, 0xfc, 0x25, + 0x7a, 0x70, 0xad, 0x0b, 0x61, 0x31, 0x15, 0x02, 0x12, 0x7f, 0xb5, 0xef, 0x0d, 0xd7, 0x83, 0x5e, + 0x74, 0x8d, 0xc9, 0x61, 0x45, 0xc3, 0x4f, 0xd0, 0x76, 0xa6, 0x64, 0xc1, 0x23, 0x50, 0x64, 0x0a, + 0x40, 0x32, 0x29, 0x13, 0x42, 0xa3, 0x48, 0x11, 0x6d, 0x94, 0x7f, 0xc3, 0x99, 0x6c, 0x35, 0x8c, + 0xa7, 0x00, 0x13, 0x29, 0x93, 0xfd, 0x28, 0x52, 0x27, 0x46, 0xe1, 0xef, 0x10, 0x66, 0xac, 0x20, + 0xb6, 0x28, 0x32, 0x37, 0x36, 0x3b, 0x2e, 0x23, 0xff, 0x66, 0xdf, 0x1b, 0xb6, 0x77, 0xef, 0x8f, + 0xaa, 0xda, 0x8d, 0x9a, 0xda, 0x8d, 0x8e, 0xea, 0xda, 0x1e, 0xac, 0xbd, 0x7a, 0xd3, 0x5b, 0xf9, + 0xf5, 0xaf, 0x9e, 0x17, 0x6c, 0x32, 0x56, 0x9c, 0x56, 0xea, 0x89, 0x13, 0xe3, 0x1f, 0xd1, 0x3d, + 0x97, 0xcd, 0x14, 0xd4, 0x55, 0xdf, 0xd6, 0xfb, 0xfb, 0x7e, 0xdc, 0x78, 0x2c, 0x9b, 0x3f, 0x43, + 0xfd, 0xa6, 0xdf, 0x88, 0x82, 0xa5, 0x12, 0x4e, 0x15, 0x65, 0x76, 0xe1, 0xdf, 0x72, 0x19, 0x77, + 0x1b, 0x5e, 0xb0, 0x44, 0x7b, 0x5a, 0xb3, 0xf0, 0x0e, 0xc2, 0x31, 0xd7, 0x46, 0x2a, 0xce, 0x68, + 0x42, 0x40, 0x18, 0xc5, 0x41, 0xfb, 0x6b, 0xee, 0x02, 0xef, 0xbc, 0x45, 0xbe, 0xa8, 0x00, 0xfc, + 0x2d, 0xda, 0xcc, 0x45, 0x28, 0x45, 0xc4, 0xc5, 0xac, 0x49, 0x67, 0xfd, 0xfd, 0xd3, 0xf9, 0x68, + 0x2e, 0xae, 0x13, 0xd9, 0x43, 0x5b, 0x5a, 0x4e, 0x0d, 0x91, 0x99, 0x21, 0xb6, 0x42, 0x26, 0x56, + 0xa0, 0x63, 0x99, 0x44, 0x3e, 0x72, 0xc7, 0xbf, 0x6b, 0xd1, 0xe3, 0xcc, 0x1c, 0xe7, 0xe6, 0xb4, + 0x81, 0xf0, 0x43, 0x74, 0x5b, 0xc1, 0x39, 0x55, 0x11, 0x89, 0x40, 0xc8, 0x54, 0xfb, 0xed, 0xfe, + 0xea, 0x70, 0x3d, 0xd8, 0xa8, 0x36, 0x8f, 0xdc, 0x1e, 0xfe, 0x14, 0xcd, 0x2f, 0x9b, 0x2c, 0xb3, + 0x37, 0x1c, 0xbb, 0xd3, 0xa0, 0xc1, 0x82, 0x6a, 0xf0, 0x19, 0xfa, 0xe4, 0x6b, 0xaa, 0xcd, 0x62, + 0x7f, 0x1d, 0xd8, 0x2e, 0x7e, 0x06, 0x7c, 0x16, 0x1b, 0xbc, 0x85, 0x5a, 0xb1, 0x5b, 0xb9, 0x97, + 0xb1, 0x1a, 0xd4, 0xd1, 0xe0, 0x37, 0x0f, 0xdd, 0x3d, 0x54, 0x52, 0xeb, 0x43, 0xfb, 0xe6, 0x5f, + 0xd0, 0x84, 0x47, 0xd4, 0x48, 0x65, 0x9f, 0x92, 0xed, 0x40, 0xd0, 0xda, 0x09, 0x36, 0x82, 0x26, + 0xc4, 0x1d, 0x74, 0x33, 0x93, 0xe7, 0xa0, 0xea, 0xb7, 0x52, 0x05, 0x98, 0xa2, 0x56, 0x96, 0x87, + 0x67, 0x50, 0xba, 0xa6, 0x6f, 0xef, 0x76, 0xde, 0x29, 0xea, 0xbe, 0x28, 0x0f, 0xf6, 0xfe, 0x79, + 0xd3, 0xbb, 0x57, 0xd2, 0x34, 0x79, 0x32, 0xb0, 0xb7, 0x0b, 0x42, 0xe7, 0x9a, 0x54, 0xba, 0xc1, + 0x1f, 0xbf, 0xef, 0x74, 0xea, 0xc9, 0xc0, 0x54, 0x99, 0x19, 0x39, 0x9a, 0xe4, 0xe1, 0x57, 0x50, + 0x06, 0xb5, 0xf1, 0xc0, 0xa0, 0x3b, 0xdf, 0x50, 0x93, 0x2b, 0x2e, 0x66, 0x2f, 0x4e, 0x0e, 0x27, + 0x94, 0x9d, 0x81, 0xb1, 0xa7, 0x29, 0x34, 0x7b, 0x5e, 0x3d, 0xf8, 0x1b, 0x41, 0x15, 0xe0, 0xe7, + 0xe8, 0x76, 0xea, 0xa8, 0xa6, 0x74, 0x2d, 0xec, 0xce, 0xda, 0xde, 0xdd, 0x7e, 0xe7, 0x50, 0xa7, + 0xcd, 0x30, 0xa9, 0xae, 0xfa, 0xa5, 0xbd, 0xea, 0x8d, 0x46, 0x6a, 0xc1, 0xc1, 0xcf, 0xa8, 0x7d, + 0x92, 0x50, 0x1d, 0x07, 0xc0, 0xa4, 0x8a, 0xf0, 0x10, 0x6d, 0x9e, 0x53, 0x6e, 0x6c, 0x13, 0x49, + 0x41, 0x14, 0x64, 0x49, 0x59, 0xcf, 0x9a, 0x0f, 0xeb, 0xfd, 0x63, 0x11, 0xd8, 0x5d, 0xbc, 0x8f, + 0xd6, 0x35, 0x88, 0xe8, 0xff, 0x7f, 0x7f, 0xcd, 0xca, 0x2c, 0x70, 0xf0, 0xfd, 0xab, 0x8b, 0xae, + 0xf7, 0xfa, 0xa2, 0xeb, 0xfd, 0x7d, 0xd1, 0xf5, 0x5e, 0x5e, 0x76, 0x57, 0x5e, 0x5f, 0x76, 0x57, + 0xfe, 0xbc, 0xec, 0xae, 0xfc, 0xf0, 0xf9, 0x8c, 0x9b, 0x38, 0x0f, 0x47, 0x4c, 0xa6, 0xf5, 0x38, + 0x1d, 0xbf, 0x9d, 0xdc, 0x3b, 0xf3, 0xc9, 0x5d, 0xec, 0x8d, 0x7f, 0x5a, 0xfe, 0x2f, 0x98, 0x32, + 0x03, 0x1d, 0xb6, 0xdc, 0x01, 0xf6, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x61, 0xb7, 0xcd, 0x97, + 0x48, 0x06, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -643,6 +701,47 @@ func (m *MaturingVSCPacket) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SlashRecord) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SlashRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SlashRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n6, err6 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.SendTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.SendTime):]) + if err6 != nil { + return 0, err6 + } + i -= n6 + i = encodeVarintConsumer(dAtA, i, uint64(n6)) + i-- + dAtA[i] = 0x12 + if m.WaitingOnReply { + i-- + if m.WaitingOnReply { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintConsumer(dAtA []byte, offset int, v uint64) int { offset -= sovConsumer(v) base := offset @@ -752,6 +851,20 @@ func (m *MaturingVSCPacket) Size() (n int) { return n } +func (m *SlashRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.WaitingOnReply { + n += 2 + } + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.SendTime) + n += 1 + l + sovConsumer(uint64(l)) + return n +} + func sovConsumer(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1467,6 +1580,109 @@ func (m *MaturingVSCPacket) Unmarshal(dAtA []byte) error { } return nil } +func (m *SlashRecord) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsumer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SlashRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SlashRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WaitingOnReply", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsumer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.WaitingOnReply = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SendTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsumer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsumer + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsumer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.SendTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsumer(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsumer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipConsumer(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index 8b792419ef..b755cf9f5a 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -102,6 +102,9 @@ const ( // This index is used for implementing a FIFO queue of pending packets in the KV store. PendingPacketsIndexByteKey + // SlashRecordByteKey is the single byte key storing the consumer's slash record. + SlashRecordByteKey + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go ) @@ -218,6 +221,11 @@ func PendingPacketsIndexKey() []byte { return []byte{PendingPacketsIndexByteKey} } +// SlashRecordKey returns the key storing the consumer's slash record. +func SlashRecordKey() []byte { + return []byte{SlashRecordByteKey} +} + // NOTE: DO NOT ADD FULLY DEFINED KEY FUNCTIONS WITHOUT ADDING THEM TO getAllFullyDefinedKeys() IN keys_test.go // diff --git a/x/ccv/consumer/types/keys_test.go b/x/ccv/consumer/types/keys_test.go index 5290dd3599..a8cebee284 100644 --- a/x/ccv/consumer/types/keys_test.go +++ b/x/ccv/consumer/types/keys_test.go @@ -42,6 +42,7 @@ func getAllKeyPrefixes() []byte { StandaloneTransferChannelIDByteKey, PrevStandaloneChainByteKey, PendingPacketsIndexByteKey, + SlashRecordByteKey, } } @@ -79,5 +80,6 @@ func getAllFullyDefinedKeys() [][]byte { StandaloneTransferChannelIDKey(), PrevStandaloneChainKey(), PendingPacketsIndexKey(), + SlashRecordKey(), } } diff --git a/x/ccv/consumer/types/throttle_retry.go b/x/ccv/consumer/types/throttle_retry.go new file mode 100644 index 0000000000..9ea179ffe4 --- /dev/null +++ b/x/ccv/consumer/types/throttle_retry.go @@ -0,0 +1,11 @@ +package types + +import time "time" + +// NewSlashRecord creates a new slash record +func NewSlashRecord(sendTime time.Time, waitingOnReply bool) (record SlashRecord) { + return SlashRecord{ + SendTime: sendTime, + WaitingOnReply: true, + } +} diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index df4fdb98ce..d63594dad1 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -44,7 +44,7 @@ func (k Keeper) OnRecvVSCMaturedPacket( "vscID", data.ValsetUpdateId, ) - ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + ack := channeltypes.NewResultAcknowledgement(ccv.V1Result) return ack } @@ -355,7 +355,7 @@ func (k Keeper) OnRecvSlashPacket(ctx sdk.Context, packet channeltypes.Packet, d // return successful ack, as an error would result // in the consumer closing the CCV channel - return channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + return channeltypes.NewResultAcknowledgement(ccv.V1Result) } // Queue a slash entry to the global queue, which will be seen by the throttling logic @@ -379,7 +379,7 @@ func (k Keeper) OnRecvSlashPacket(ctx sdk.Context, packet channeltypes.Packet, d "infractionType", data.Infraction, ) - return channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + return channeltypes.NewResultAcknowledgement(ccv.V1Result) } // ValidateSlashPacket validates a recv slash packet before it is diff --git a/x/ccv/types/ccv.go b/x/ccv/types/ccv.go index 70921704f7..5b4e57994f 100644 --- a/x/ccv/types/ccv.go +++ b/x/ccv/types/ccv.go @@ -157,6 +157,20 @@ func (vdt1 SlashPacketDataV1) FromV1() *SlashPacketData { } } +type PacketAckResult []byte + +var ( // slice types can't be const + + // The result ack that has historically been sent from the provider. + // A provider with v1 throttling sends these acks for all successfully recv packets. + V1Result = PacketAckResult([]byte{byte(1)}) + // Slash packet handled result ack, sent by a throttling v2 provider to indicate that a slash packet was handled. + SlashPacketHandledResult = PacketAckResult([]byte{byte(2)}) + // Slash packet bounced result ack, sent by a throttling v2 provider to indicate that a slash packet was NOT handled + // and should eventually be retried. + SlashPacketBouncedResult = PacketAckResult([]byte{byte(3)}) +) + // An exported wrapper around the auto generated isConsumerPacketData_Data interface, only for // AppendPendingPacket to accept the interface as an argument. type ExportedIsConsumerPacketData_Data interface { From 64d860d10d6055950dd0672f7c3b8b2790d0f0a5 Mon Sep 17 00:00:00 2001 From: bernd-m <43466467+bermuell@users.noreply.github.com> Date: Fri, 11 Aug 2023 11:38:51 +0200 Subject: [PATCH 38/38] Tests: 1175 ci refactor e2e (#1191) * Refactor E2E Tests * Update docstring for short-happy-path The old docstring was outdated and since modified on main --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --- .github/workflows/manual-e2e.yml | 91 ++++++++++++++-- .github/workflows/nightly-e2e.yml | 104 ++++++++++++++++-- Makefile | 12 +-- tests/e2e/main.go | 172 +++++++++++++++++++++--------- tests/e2e/steps.go | 2 +- 5 files changed, 311 insertions(+), 70 deletions(-) diff --git a/.github/workflows/manual-e2e.yml b/.github/workflows/manual-e2e.yml index 899e4ba230..388a19f0f5 100644 --- a/.github/workflows/manual-e2e.yml +++ b/.github/workflows/manual-e2e.yml @@ -5,22 +5,99 @@ on: workflow_dispatch: jobs: - manual-integration-main: + happy-path-test: runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 20 steps: - uses: actions/setup-go@v4 with: go-version: "1.20" - uses: actions/checkout@v3 - - name: Checkout LFS objects run: git lfs checkout - - name: Setup Go uses: actions/setup-go@v4 with: go-version: "1.20" # The Go version to download (if necessary) and use. - - - name: E2E tests - run: make test-e2e + - name: E2E happy-path test + run: go run ./tests/e2e/... --tc happy-path + changeover-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E changeover test + run: go run ./tests/e2e/... --tc changeover + democracy-reward-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E democracy-reward tests + run: go run ./tests/e2e/... --tc democracy-reward + democracy-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E democracy tests + run: go run ./tests/e2e/... --tc democracy + slash-throttle-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E slash-throttle tests + run: go run ./tests/e2e/... --tc slash-throttle + multiconsumer-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E multi-consumer tests + run: go run ./tests/e2e/... --tc multiconsumer diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index f69125e6b8..cd7f155e12 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -18,21 +18,111 @@ on: - cron: "0 3 * * *" jobs: - nightly-test: + happy-path-test: runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 20 steps: - uses: actions/setup-go@v4 with: go-version: "1.20" - - uses: actions/checkout@v3 - - - name: E2E tests - run: make test-e2e + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E happy-path test + run: go run ./tests/e2e/... --tc happy-path + changeover-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E changeover test + run: go run ./tests/e2e/... --tc changeover + democracy-reward-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E democracy-reward tests + run: go run ./tests/e2e/... --tc democracy-reward + democracy-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E democracy tests + run: go run ./tests/e2e/... --tc democracy + slash-throttle-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E slash-throttle tests + run: go run ./tests/e2e/... --tc slash-throttle + multiconsumer-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v4 + with: + go-version: "1.20" + - uses: actions/checkout@v3 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.20" # The Go version to download (if necessary) and use. + - name: E2E multi-consumer tests + run: go run ./tests/e2e/... --tc multiconsumer nightly-test-fail: - needs: nightly-test + needs: + - happy-path-test + - changeover-test + - democracy-reward-test + - democracy-test + - slash-throttle-test + - multiconsumer-test if: ${{ failure() }} runs-on: ubuntu-latest steps: diff --git a/Makefile b/Makefile index edbacbc0e0..ac7c38f068 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,8 @@ install: go.sum go install $(BUILD_FLAGS) ./cmd/interchain-security-sd # run all tests: unit, integration, diff, and E2E -test: - go test ./... && go run ./tests/e2e/... +test: + go test ./... && go run ./tests/e2e/... # run all unit tests test-unit: @@ -31,12 +31,12 @@ test-diff: # run only happy path E2E tests test-e2e-short: - go run ./tests/e2e/... --happy-path-only + go run ./tests/e2e/... --tc happy-path # run only happy path E2E tests with cometmock # this set of traces does not test equivocation but it does check downtime test-e2e-short-cometmock: - go run ./tests/e2e/... --cometmock-happy-path --use-cometmock --use-gorelayer + go run ./tests/e2e/... --tc happy-path-short --use-cometmock --use-gorelayer # run full E2E tests in sequence (including multiconsumer) test-e2e-multi-consumer: @@ -52,7 +52,7 @@ test-gaia-e2e: # run only happy path E2E tests using latest tagged gaia test-gaia-e2e-short: - go run ./tests/e2e/... --happy-path-only --use-gaia + go run ./tests/e2e/... --tc happy-path --use-gaia # run full E2E tests in parallel (including multiconsumer) using latest tagged gaia test-gaia-e2e-parallel: @@ -66,7 +66,7 @@ test-gaia-e2e-tagged: # run only happy path E2E tests using latest tagged gaia # usage: GAIA_TAG=v9.0.0 make test-gaia-e2e-short-tagged test-gaia-e2e-short-tagged: - go run ./tests/e2e/... --happy-path-only --use-gaia --gaia-tag $(GAIA_TAG) + go run ./tests/e2e/... --tc happy-path --use-gaia --gaia-tag $(GAIA_TAG) # run full E2E tests in parallel (including multiconsumer) using specific tagged version of gaia # usage: GAIA_TAG=v9.0.0 make test-gaia-e2e-parallel-tagged diff --git a/tests/e2e/main.go b/tests/e2e/main.go index 58bb065c26..e9336422ae 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -15,13 +15,26 @@ import ( "github.com/kylelemons/godebug/pretty" ) +// The list of test cases to be executed +type TestSet []string + +func (t *TestSet) Set(value string) (err error) { + // Check and skip duplicates + for _, v := range *t { + if v == value { + return + } + } + *t = append(*t, value) + return +} + +func (t *TestSet) String() string { + return fmt.Sprint(*t) +} + var ( - verbose = flag.Bool("verbose", false, "turn verbose logging on/off") - happyPathOnly = flag.Bool("happy-path-only", false, "run happy path tests only") - cometmockCompatibleHappyPath = flag.Bool("cometmock-happy-path", false, `run cometmock compatible happy path tests only. -This is like the happy path, but skips steps -that involve starting or stopping nodes for the same chain outside of the chain setup or teardown. -This is suited for CometMock+Gorelayer testing`) + verbose = flag.Bool("verbose", false, "turn verbose logging on/off") includeMultiConsumer = flag.Bool("include-multi-consumer", false, "include multiconsumer tests in run") parallel = flag.Bool("parallel", false, "run all tests in parallel") localSdkPath = flag.String("local-sdk-path", "", @@ -35,59 +48,119 @@ var ( gaiaTag = flag.String("gaia-tag", "", "gaia tag to use - default is latest") ) -// runs E2E tests -// all docker containers are built sequentially to avoid race conditions when using local cosmos-sdk -// after building docker containers, all tests are run in parallel using their respective docker containers -func main() { - flag.Parse() - - if cometmockCompatibleHappyPath != nil && *cometmockCompatibleHappyPath { - fmt.Println("=============== running short happy path only ===============") - tr := DefaultTestRun() - tr.Run(cometmockCompatibleHappyPathSteps, *localSdkPath, *useGaia, *gaiaTag) - return - } - - if happyPathOnly != nil && *happyPathOnly { - fmt.Println("=============== running happy path only ===============") - tr := DefaultTestRun() - tr.Run(happyPathSteps, *localSdkPath, *useGaia, *gaiaTag) - return - } - - testRuns := []testRunWithSteps{ - {ChangeoverTestRun(), changeoverSteps}, - {DefaultTestRun(), happyPathSteps}, - {DemocracyTestRun(true), democracySteps}, - {DemocracyTestRun(false), rewardDenomConsumerSteps}, - {SlashThrottleTestRun(), slashThrottleSteps}, - } - if includeMultiConsumer != nil && *includeMultiConsumer { - testRuns = append(testRuns, testRunWithSteps{MultiConsumerTestRun(), multipleConsumers}) +var ( + testSelection TestSet + testMap map[string]*testRunWithSteps = map[string]*testRunWithSteps{ + "happy-path-short": { + testRun: DefaultTestRun(), steps: shortHappyPathSteps, + description: `This is like the happy path, but skips steps +that involve starting or stopping nodes for the same chain outside of the chain setup or teardown. +This is suited for CometMock+Gorelayer testing`, + }, + "happy-path": {testRun: DefaultTestRun(), steps: happyPathSteps, description: "happy path tests"}, + "changeover": {testRun: ChangeoverTestRun(), steps: changeoverSteps, description: "changeover tests"}, + "democracy-reward": {testRun: DemocracyTestRun(true), steps: democracySteps, description: "democracy tests allowing rewards"}, + "democracy": {testRun: DemocracyTestRun(false), steps: rewardDenomConsumerSteps, description: "democracy tests"}, + "slash-throttle": {testRun: SlashThrottleTestRun(), steps: slashThrottleSteps, description: "slash throttle tests"}, + "multiconsumer": {testRun: MultiConsumerTestRun(), steps: multipleConsumers, description: "multi consumer tests"}, } +) - start := time.Now() +func executeTests(tests []testRunWithSteps) (err error) { if parallel != nil && *parallel { fmt.Println("=============== running all tests in parallel ===============") - var wg sync.WaitGroup - for _, run := range testRuns { + } + + var wg sync.WaitGroup + for _, testCase := range tests { + if parallel != nil && *parallel { wg.Add(1) go func(run testRunWithSteps) { defer wg.Done() - tr := run.testRun - tr.Run(run.steps, *localSdkPath, *useGaia, *gaiaTag) - }(run) + run.testRun.Run(run.steps, *localSdkPath, *useGaia, *gaiaTag) + }(testCase) + } else { + log.Printf("=============== running %s ===============\n", testCase.testRun.name) + testCase.testRun.Run(testCase.steps, *localSdkPath, *useGaia, *gaiaTag) } + } + + if parallel != nil && *parallel { wg.Wait() - fmt.Printf("TOTAL TIME ELAPSED: %v\n", time.Since(start)) - return + } + return +} + +func parseArguments() (err error) { + flag.Var(&testSelection, "tc", + fmt.Sprintf("Selection of test cases to be executed:\n%s,\n%s", + func() string { + var keys []string + for k, v := range testMap { + keys = append(keys, fmt.Sprintf("- %s : %s", k, v.description)) + } + return strings.Join(keys, "\n") + }(), + "Example: -tc multiconsumer -tc happy-path ")) + flag.Parse() + + // Enforce go-relayer in case of cometmock as hermes is not yet supported + if useCometmock != nil && *useCometmock && (useGorelayer == nil || !*useGorelayer) { + fmt.Println("Enforcing go-relayer as cometmock is requested") + if err = flag.Set("use-gorelayer", "true"); err != nil { + return + } + } + // check if specified test case exists + for _, tc := range testSelection { + if _, hasKey := testMap[tc]; !hasKey { + err := fmt.Errorf("unknown test case '%s'", tc) + return err + } + } + return +} + +func getTestCases(selection TestSet) (tests []testRunWithSteps) { + // Run default tests if no test cases were selected + if len(selection) == 0 { + selection = TestSet{ + "changeover", "happy-path", + "democracy-reward", "democracy", "slash-throttle", + } + if includeMultiConsumer != nil && *includeMultiConsumer { + selection = append(selection, "multiconsumer") + } + } + + // Get tests from selection + tests = []testRunWithSteps{} + for _, tc := range selection { + if _, exists := testMap[tc]; !exists { + log.Fatalf("Test case '%s' not found", tc) + } + tests = append(tests, *testMap[tc]) + } + return +} + +// runs E2E tests +// all docker containers are built sequentially to avoid race conditions when using local cosmos-sdk +// after building docker containers, all tests are run in parallel using their respective docker containers +func main() { + if err := parseArguments(); err != nil { + flag.Usage() + log.Fatalf("Error parsing command arguments %s\n", err) } - for _, run := range testRuns { - tr := run.testRun - tr.Run(run.steps, *localSdkPath, *useGaia, *gaiaTag) + testCases := getTestCases(testSelection) + + start := time.Now() + err := executeTests(testCases) + if err != nil { + log.Fatalf("Test execution failed '%s'", err) } - fmt.Printf("TOTAL TIME ELAPSED: %v\n", time.Since(start)) + log.Printf("TOTAL TIME ELAPSED: %v\n", time.Since(start)) } // Run sets up docker container and executes the steps in the test run. @@ -104,8 +177,9 @@ func (tr *TestRun) Run(steps []Step, localSdkPath string, useGaia bool, gaiaTag } type testRunWithSteps struct { - testRun TestRun - steps []Step + testRun TestRun + steps []Step + description string } func (tr *TestRun) runStep(step Step, verbose bool) { diff --git a/tests/e2e/steps.go b/tests/e2e/steps.go index 770ac45dda..b33d19783a 100644 --- a/tests/e2e/steps.go +++ b/tests/e2e/steps.go @@ -31,7 +31,7 @@ var happyPathSteps = concatSteps( stepsStopChain("consu", 4), // stop chain ) -var cometmockCompatibleHappyPathSteps = concatSteps( +var shortHappyPathSteps = concatSteps( stepsStartChains([]string{"consu"}, false), stepsDelegate("consu"), stepsUnbond("consu"),