From ea7ce035b86c5bbd044fc6a7277a011ab9aa02fb Mon Sep 17 00:00:00 2001 From: Reece Williams <31943163+Reecepbcups@users.noreply.github.com> Date: Tue, 10 Oct 2023 23:51:23 -0500 Subject: [PATCH] local-interchain: Rust Driver (#785) * cli: add app overrides for port and address * init: rust client base * request resp: use JSON * minor: fmt * bin builder * feat: add query & binary actions + simple examples * Add cw_std * feat: tx, get_tx_hash, and query_tx_hash * add: localic-std library * condense main.rs to use new std lib * use file_path for uploader instead * lib misc: bank_send, errors, libs, bals * CW: Store, Init, Query, Better ActionBuilder * minor: test path cleanup * ci: attempt basic local-ic + rust e2e * ci: set manifest-path to sub-dir * ci: fix manifest path * cw: get_contract_address * cw store: if err, return exact (may not be JSON) * add execute_contract * feat: Upload-Type for cosmwasm contracts * better uploader error handling for debugging * more test, get_files func, * backup; more info queries * move GetInfo into their own funcs * query: get ibc config (denom, bin, etc) * minor: more general test for node name * recover-key, overwrite genesis, add-full-node * format * add relayer support * upgrade old docker values * tweaks, relayer Channel type * CW: instantiate, execute, and query with mut CW object * move CW & Bank -> a modules mod * on Tx, poll for Tx hash resp. 5x before failure * codebase lint * ci: clippy lint workflow * Move dump-contract-state to POST * ci: fix lint * add: `get_sdk_status_code` * ci: change clippy working-directory * `query_value`, Debug for CRBuilder * remove old println, add err to fet_files * migrate to a workspace * mod tidy * add back flags to start_chain * abstract away required flags * remove old panic, fmt * move old rest server to be cli arg only * Add setup documentation --- .github/workflows/lint.yml | 21 + .github/workflows/local-interchain.yaml | 54 +- local-interchain/.gitignore | 1 + local-interchain/README.md | 9 +- local-interchain/chains/base.json | 15 +- local-interchain/chains/base_ibc.json | 4 +- local-interchain/chains/cosmoshub.json | 3 +- local-interchain/chains/juno_ibc.json | 15 +- local-interchain/chains/stargaze.json | 3 +- local-interchain/cmd/local-ic/start_chain.go | 18 +- local-interchain/configs/server.json | 6 - .../contracts/cw_ibc_example.wasm | Bin 0 -> 201305 bytes local-interchain/docs/REST_API.md | 2 +- local-interchain/go.mod | 44 +- local-interchain/go.sum | 121 +- local-interchain/interchain/config.go | 2 - .../interchain/handlers/actions.go | 86 +- local-interchain/interchain/handlers/info.go | 134 +- local-interchain/interchain/handlers/types.go | 43 + .../interchain/handlers/uploader.go | 41 +- local-interchain/interchain/router/router.go | 15 +- local-interchain/interchain/start.go | 22 +- local-interchain/rust/.gitignore | 1 + local-interchain/rust/Cargo.lock | 1683 +++++++++++++++++ local-interchain/rust/Cargo.toml | 11 + local-interchain/rust/Makefile | 6 + local-interchain/rust/README.md | 227 +++ local-interchain/rust/localic-std/Cargo.toml | 16 + local-interchain/rust/localic-std/Makefile | 6 + .../rust/localic-std/src/errors.rs | 43 + .../rust/localic-std/src/filesystem.rs | 25 + local-interchain/rust/localic-std/src/lib.rs | 10 + .../rust/localic-std/src/modules/bank.rs | 41 + .../rust/localic-std/src/modules/cosmwasm.rs | 319 ++++ .../rust/localic-std/src/modules/gov.rs | 9 + .../rust/localic-std/src/modules/mod.rs | 3 + local-interchain/rust/localic-std/src/node.rs | 196 ++ .../rust/localic-std/src/polling.rs | 21 + .../rust/localic-std/src/relayer.rs | 166 ++ .../rust/localic-std/src/transactions.rs | 385 ++++ .../rust/localic-std/src/types.rs | 131 ++ local-interchain/rust/main/Cargo.toml | 18 + local-interchain/rust/main/src/base.rs | 41 + local-interchain/rust/main/src/main.rs | 314 +++ local-interchain/scripts/helpers/cosmwasm.py | 9 +- 45 files changed, 4258 insertions(+), 82 deletions(-) delete mode 100644 local-interchain/configs/server.json create mode 100644 local-interchain/contracts/cw_ibc_example.wasm create mode 100644 local-interchain/interchain/handlers/types.go create mode 100644 local-interchain/rust/.gitignore create mode 100644 local-interchain/rust/Cargo.lock create mode 100644 local-interchain/rust/Cargo.toml create mode 100644 local-interchain/rust/Makefile create mode 100644 local-interchain/rust/README.md create mode 100644 local-interchain/rust/localic-std/Cargo.toml create mode 100644 local-interchain/rust/localic-std/Makefile create mode 100644 local-interchain/rust/localic-std/src/errors.rs create mode 100644 local-interchain/rust/localic-std/src/filesystem.rs create mode 100644 local-interchain/rust/localic-std/src/lib.rs create mode 100644 local-interchain/rust/localic-std/src/modules/bank.rs create mode 100644 local-interchain/rust/localic-std/src/modules/cosmwasm.rs create mode 100644 local-interchain/rust/localic-std/src/modules/gov.rs create mode 100644 local-interchain/rust/localic-std/src/modules/mod.rs create mode 100644 local-interchain/rust/localic-std/src/node.rs create mode 100644 local-interchain/rust/localic-std/src/polling.rs create mode 100644 local-interchain/rust/localic-std/src/relayer.rs create mode 100644 local-interchain/rust/localic-std/src/transactions.rs create mode 100644 local-interchain/rust/localic-std/src/types.rs create mode 100644 local-interchain/rust/main/Cargo.toml create mode 100644 local-interchain/rust/main/src/base.rs create mode 100644 local-interchain/rust/main/src/main.rs diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index eec956ccc..54ba68e15 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,3 +18,24 @@ jobs: version: v1.54 only-new-issues: true args: --timeout=5m + + clippy-lint: + defaults: + run: + working-directory: local-interchain/rust/localic-std + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install stable with clippy and rustfmt + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + components: rustfmt, clippy + - name: Install clippy + run: rustup component add clippy + - name: Update + run: cargo update + - name: Run clippy + run: make lint + diff --git a/.github/workflows/local-interchain.yaml b/.github/workflows/local-interchain.yaml index 4f1815ab1..8e94e8748 100644 --- a/.github/workflows/local-interchain.yaml +++ b/.github/workflows/local-interchain.yaml @@ -15,6 +15,9 @@ jobs: build: runs-on: ubuntu-latest name: build + defaults: + run: + working-directory: ./local-interchain steps: - name: Checkout interchaintest uses: actions/checkout@v4 @@ -24,4 +27,53 @@ jobs: with: go-version: ${{ env.GO_VERSION }} - - run: cd local-interchain && go mod tidy && make install \ No newline at end of file + - name: build local-interchain + run: go mod tidy && make install + + - name: Upload localic artifact + uses: actions/upload-artifact@v3 + with: + name: local-ic + path: ~/go/bin/local-ic + + # TOOO: put the python workflow here. (https://github.com/strangelove-ventures/interchaintest/pull/775) + + rust-e2e: + name: rust e2e + needs: build + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./local-interchain + strategy: + fail-fast: false + + steps: + - name: checkout chain + uses: actions/checkout@v3 + + - name: Install latest toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + target: wasm32-unknown-unknown + override: true + + - name: Download Tarball Artifact + uses: actions/download-artifact@v3 + with: + name: local-ic + path: /tmp + + - name: Make local-ic executable + run: chmod +x /tmp/local-ic + + - name: Start background ibc local-interchain + run: /tmp/local-ic start juno_ibc --api-port 8080 & + + - name: Run Rust Script + run: cd rust && cargo run --package localic-bin --bin localic-bin + + - name: Cleanup + run: killall local-ic && exit 0 \ No newline at end of file diff --git a/local-interchain/.gitignore b/local-interchain/.gitignore index 278cae904..cd3b389c3 100644 --- a/local-interchain/.gitignore +++ b/local-interchain/.gitignore @@ -9,6 +9,7 @@ configs/logs.json configs/contracts.json contracts/*.wasm +!contracts/cw_ibc_example.wasm __pycache__/ \ No newline at end of file diff --git a/local-interchain/README.md b/local-interchain/README.md index 451951c88..ddaf28dc5 100644 --- a/local-interchain/README.md +++ b/local-interchain/README.md @@ -2,7 +2,11 @@ A simple way to config and run IBC local chain testing environments with any language of choice for interaction. -## Installing +## Repository CI + +- [**Rust**](./rust/README.md) + +## Installing Locally **Install on Mac / Linux** ```bash @@ -102,8 +106,7 @@ Here is a base chain template with every feature the configuration accepts. Acco "gas_prices": "0%DENOM%", "gas_adjustment": 2.0, "number_vals": 1, - "number_node": 0, - "use_new_genesis_command": false, + "number_node": 0, "ibc_paths": ["juno-ibc-1"], "debugging": true, "block_time": "500ms", diff --git a/local-interchain/chains/base.json b/local-interchain/chains/base.json index b07c762f4..c7c9197e7 100644 --- a/local-interchain/chains/base.json +++ b/local-interchain/chains/base.json @@ -7,10 +7,9 @@ "binary": "junod", "bech32_prefix": "juno", "docker_image": { - "repository": "ghcr.io/cosmoscontracts/juno-e2e", - "version": "v14.1.0" - }, - "use_new_genesis_command": false, + "repository": "ghcr.io/cosmoscontracts/juno", + "version": "v17.0.0" + }, "gas_prices": "0%DENOM%", "chain_type": "cosmos", "coin_type": 118, @@ -24,19 +23,19 @@ "genesis": { "modify": [ { - "key": "app_state.gov.voting_params.voting_period", + "key": "app_state.gov.params.voting_period", "value": "15s" }, { - "key": "app_state.gov.deposit_params.max_deposit_period", + "key": "app_state.gov.params.max_deposit_period", "value": "15s" }, { - "key": "app_state.gov.deposit_params.min_deposit.0.denom", + "key": "app_state.gov.params.min_deposit.0.denom", "value": "ujuno" }, { - "key": "app_state.gov.deposit_params.min_deposit.0.amount", + "key": "app_state.gov.params.min_deposit.0.amount", "value": "1" } ], diff --git a/local-interchain/chains/base_ibc.json b/local-interchain/chains/base_ibc.json index 45d430625..878c884fb 100644 --- a/local-interchain/chains/base_ibc.json +++ b/local-interchain/chains/base_ibc.json @@ -31,8 +31,8 @@ "bech32_prefix": "juno", "denom": "ujuno", "docker_image": { - "repository": "ghcr.io/cosmoscontracts/juno-e2e", - "version": "v14.1.0" + "repository": "ghcr.io/cosmoscontracts/juno", + "version": "v17.0.0" }, "block_time": "2s", "encoding-options": ["juno"], diff --git a/local-interchain/chains/cosmoshub.json b/local-interchain/chains/cosmoshub.json index 93e343854..23edf037b 100644 --- a/local-interchain/chains/cosmoshub.json +++ b/local-interchain/chains/cosmoshub.json @@ -8,8 +8,7 @@ "bech32_prefix": "cosmos", "docker_image": { "version": "v10.0.1" - }, - "use_new_genesis_command": false, + }, "gas_prices": "0%DENOM%", "chain_type": "cosmos", "coin_type": 118, diff --git a/local-interchain/chains/juno_ibc.json b/local-interchain/chains/juno_ibc.json index dc7409c45..f22cdaddc 100644 --- a/local-interchain/chains/juno_ibc.json +++ b/local-interchain/chains/juno_ibc.json @@ -7,10 +7,9 @@ "binary": "junod", "bech32_prefix": "juno", "docker_image": { - "repository": "ghcr.io/cosmoscontracts/juno-e2e", - "version": "v14.1.0" + "repository": "ghcr.io/cosmoscontracts/juno", + "version": "v17.0.0" }, - "use_new_genesis_command": false, "gas_prices": "0%DENOM%", "chain_type": "cosmos", "coin_type": 118, @@ -25,15 +24,15 @@ "genesis": { "modify": [ { - "key": "app_state.gov.voting_params.voting_period", + "key": "app_state.gov.params.voting_period", "value": "15s" }, { - "key": "app_state.gov.deposit_params.max_deposit_period", + "key": "app_state.gov.params.max_deposit_period", "value": "15s" }, { - "key": "app_state.gov.deposit_params.min_deposit.0.denom", + "key": "app_state.gov.params.min_deposit.0.denom", "value": "ujuno" } ], @@ -60,8 +59,8 @@ "bech32_prefix": "juno", "denom": "ujuno", "docker_image": { - "repository": "ghcr.io/cosmoscontracts/juno-e2e", - "version": "v14.1.0" + "repository": "ghcr.io/cosmoscontracts/juno", + "version": "v17.0.0" }, "genesis": { "accounts": [ diff --git a/local-interchain/chains/stargaze.json b/local-interchain/chains/stargaze.json index 778424bfe..331372f82 100644 --- a/local-interchain/chains/stargaze.json +++ b/local-interchain/chains/stargaze.json @@ -8,8 +8,7 @@ "bech32_prefix": "stars", "docker_image": { "version": "v10.0.1" - }, - "use_new_genesis_command": false, + }, "gas_prices": "0%DENOM%", "chain_type": "cosmos", "coin_type": 118, diff --git a/local-interchain/cmd/local-ic/start_chain.go b/local-interchain/cmd/local-ic/start_chain.go index 8bcd95b22..e53071e62 100644 --- a/local-interchain/cmd/local-ic/start_chain.go +++ b/local-interchain/cmd/local-ic/start_chain.go @@ -8,6 +8,11 @@ import ( "github.com/strangelove-ventures/localinterchain/interchain" ) +const ( + FlagAPIAddressOverride = "api-address" + FlagAPIPortOverride = "api-port" +) + var startCmd = &cobra.Command{ Use: "start ", Aliases: []string{"s", "run"}, @@ -27,6 +32,17 @@ var startCmd = &cobra.Command{ configPath = filepath.Base(configPath) } - interchain.StartChain(parentDir, configPath) + apiAddr, _ := cmd.Flags().GetString(FlagAPIAddressOverride) + apiPort, _ := cmd.Flags().GetUint16(FlagAPIPortOverride) + + interchain.StartChain(parentDir, configPath, &interchain.AppConfig{ + Address: apiAddr, + Port: apiPort, + }) }, } + +func init() { + startCmd.Flags().String(FlagAPIAddressOverride, "127.0.0.1", "override the default API address") + startCmd.Flags().Uint16(FlagAPIPortOverride, 8080, "override the default API port") +} diff --git a/local-interchain/configs/server.json b/local-interchain/configs/server.json deleted file mode 100644 index ab05006d0..000000000 --- a/local-interchain/configs/server.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "server": { - "host": "127.0.0.1", - "port": "8080" - } -} \ No newline at end of file diff --git a/local-interchain/contracts/cw_ibc_example.wasm b/local-interchain/contracts/cw_ibc_example.wasm new file mode 100644 index 0000000000000000000000000000000000000000..43448ef74f96a08a8e5677d0b25ecad9f2d0cdcf GIT binary patch literal 201305 zcmeFa3zQw@b>~};^Xfi*T1QAgO9)fvAmdg-FUuJ$3C!4C3gR(l5}eD;#aW&OGAKbU z%%hPBp36XE1eV8gOcESq$FUWg3?zmGJ2=L#SWfVbV&g=~FtIWwv3!FQ*1CACOf(5u z!IS&@@BLMsI<0OY34EQk=AuPcovQD--+O<%zAxH*^V{MmisHTTimQ@4?uhTu-{`9J z4!hSSx+>P=n420uxzu&x+OR7(O~2ueqPJfaMOU@Lld?1|MEqZrbh@U^VTb8w`{&9 zs_9pYUq3s$b^Dem*3S`p`L-Kw*)k}6^VOTLkCLM5^;>S;?o~URueoOS%3C&HxAmIM z)VJoA+A}KLaQ)V+dGqhLM3HKG*=yhaw$0a<&$>5nx%$Q*dF{GwuetJ;EwfvH`W@cM z#NhGlma7@b?3FjZ{jJ-!yd&zWrSUD-{K#ux`MSoFzIF5VtFIk;RljD-Ti^aO zKAZMUZ{ITejw`RecJtQj8Si7sCzDp3rc0Y?D~{tv+(^@ye~nbX^j{n|M@G^{tC^+{ zL{u;SOZkuANgTyVquHc*Ohx_IND`IQeIyN#kmNudqh2pec*B@)2Ka4M}r^^^`+!&4GI2s)tuhT;x_8@2C<@|`=rWY>FjZSGE%Hr}@7Z8yxmBZ=BuufKWw=Igg_6`@?! z+;Z!dtKZJu=DHyydTV#TO%LL)ceu*$VOg^myeVucDu+mfF7cZCh@5`}XMC*5>Q3yWwgL>Q~~? zYqpfXJ{&Kd-E!00x6W?4`AP;p`!g!?vC2ajW_I)T8)kX9Z0q&>`0348ZxP+yy!D3b zuiU`n(JQZnEjC}d<@#&VxP9f7*KXN-;{Oys7(W#Mdi)#l$K!|N>4_B=UVQ4Ni+=RcIKS-QyzVo}rI)<% z-=BB;+t0n>|D22O_z$<`m+gx0jNfqSyW{u7+jqqGB=3yxitmYc#&^f>if_H;#+!cj z9XHRO`-%A8303~2rsjd zJf7({)+H@Ht7RiI`Pt`1Q9FzC=)9r zWNCM)cQ>e&B|QB?vXY7^jg;L-Sz}us|68w^Cr4JyKYBQNUFa~G(V$!T+L^4`{c4;{ zGx^qJvUU^ITlt2WsVM3-^eper=*2nxIIGw0$m)50dyj7Oe~xe3)N7>E8vIRE0)(x8 z!yu{o#+l};hdoBlFQ%v-d15nnFj@b#Ebg9Uop+L~mS;1)S|0b9dY*0Tr=6&sNA;}H zR*!jfQTK^hWxJG(@@QQWjiqfyxr$Nd=g)v99%amIO>)lqWVukd=u5q7sopBp>&Ioy z<^9q1X+-;DGnqY&veEU78#bVfs!eZO9ku@uVRZ*Y#4wZ9k%2H4CgV*VDyzvfkGJ=k zAOC8XoYs$X0FbxdsJlCE&d<1|R|CW;;7Zi;gfP1GP>x_4A#%QpnUx`Rkcm299COB$Y9e%LabzZ_p5DK$v z=@~y=a0qW{J~_jmCnLk2kspIwlU&emu1_<7IUvgBfGDjdgNiO$mt-~@H4sJXqBF~x z^@NBJ>$AADvN*)4$McIrkBo%oCc|}ut`f3q((JZAdgJm(ZbLcobNV(OkMJ{V z@Gs5}c-5xfU3Od6qH?;=;C;N8R6xoiirHuV>F=*VSUz?qcjJhB_3{@sf7F8v@MN^Wa%#v|m3SelH~X zA;j}vC2=i?RAiswAU?OqOjmcGgEk0%aChBW{Rr0 znS!D%Dx$WnaABtKs%#~-p0GD$rtred6zgtKOUlg5lse+5FjKszV5X==G4Q;K1q0h_ zW8kKY5M*h0U(DpOw7P}_SO3huDY@0Gs_|ylT$3#C*XFD7+CXBenyHI>(!v^CyRHAf zHkic$-Q6*eF@OW<)qQ?VCX?a@u9>8sSmF5aFftzxBg1%DE4BTpHh4&+w&#$$Qrqxs zWF}Ieca!vW(Yx*tP+Wp`71`Qr2oM8Plv*#fD1TRqLCK59co z%w^&Ogwo}*KqU0BUwC4GsLLeK9T?PveY$Z0Cjk-ZHw33RSRuSiWl^X3)4_kGp6kvmHZA(n;k34WWk7BLUJl9vKow+f5D zS%R9oOn{YouXX>>#C(FVkX_Kk>X@)QjK&frLc(QIB8H8^1apL(7p)E@rDF1Ty%PzK zgVJ5*m8fFP*vt3nCEB!jDYP;O=^dGvtI;{8h1Pb=v^enu9= z&nLmhnkZTiW#Hn>rl?xhlo%6(go;gTl}JnQJ$O~Ng4tnJ%u3;fSt-`tpjK(6V8hTz**!f4D@85J zN|{%&5YYBI1XI8(Itfqj_hqLjN-=DwoN&0o?37;#;Ra$>gc}e;qH0Z0@{sFfGPX31 z=$Mee7(oaCWpq^&=y}$n3KSZ|mvEclS@h*6l@y+s@RN(AJ3=UFsqr()<9Dp5L-P}* zQ!E;icr;{N91nsaw0X4A9Wl1$43;5I82edAt+FGzbb>cC?T0RW%{ z$j3y4u^6M)Jxvdm_uy;PETpH92V+~4MCAk|56jX=SnfFCG=+!8C}gHQK{fg4_DeW{`s&^+PKD+^i1KJs`t@gV=}#PM#7Z61EMhfr z9acu1|JiT+mmj($Pqz2uh*<0j=uKRwVJ3OaqgcZcpCm+lupY7EzaQbN-~2w|U4fqr zb^{@f`3c87NDP?Ign5N6g?XdO60>4}D(1nD&|AKrPYIKHkE`#L6T3j3PY0NzFGdGY z6s#-qTa*sodscG#9jQ$1JxLKE+l}xgmOxnNap_OoV#M=a9O)iEr=p*adwBZ#r(gGC zIo<>p=e3@T7mLpRQ*;rJ#{E&6tHrqc8MU^P*5dA`t)*SDTFlWNQhz(+{ute;(L2j7 zif-oA4N+%ZdLwFw7*lPw*X8yQ)+)DHQTcwq#S+b*k7s%-xjxPHG_G^WOz%{#-{kiz zxc&v#GkhM&bUK~f5$2R=0{vQkHZuU`-_uM`NIdK$`3NDsv3AzU=Wf5Lm*lCmxV>Yx zUEa0Z*_h80&C9J%=N*U*W_7C|)sYAeUGxdX=F4*Y6}q|bH0q=MtZgqc+v@<w~!2~g~0$8>V6T}P9_KA9>&jP@-)Ix*8_2HRM)2r=DOQ& z8Y2->lirA7pqAISjVE!$bVu{M?%s=O+WjKjd=Zi0-Uw07Jjr8Ty<4y9ygH4i2~WK~ zgT*kn7UYU7&;mw_TBr@RKxZ<(YUl!Ku2B%5 z53vBVsl!l2?)-(jx~gjTrJK}O{(0x*r#)7kKc=5+pe!js zn<7&g+0;47hA?)L$oeQ%#7#cOvF`82K-p&~oEW}du-Q8hC34MJRcD7oI1a%8*`J|$ zhYXh&cFbH4<2vl)@*(83#+F`TURB&*|00+{4#hYHBGh+|vY^i=f759zqN;X;v+pf}z2rnVZd4<~& zONI*@Pntgt_LgcusS%RKHfHD()f+_wk!_q_h;Q1a!zNr{<0Fz>r=^iMO*;6i~Bcg zCR5QB68Ff^sE5+R?HOzHpf_s2TfLJ}Ig*M}Hi;{nB>#&D3Pp@Yy-_qnlS<>zpDPqw zG6+4q$$Pj=J&2@zCe)_8Wz;g-W6_`-(j{Ku;xMF|4JTA9bhSoUbf$HPprkJ_yljAz zCzI)ZJB;95DUYdWLpjIsJd#9aHS4BclL?G(jG&5vTCEMw)YWQJ4^6Gs#A$U?t2I}v z_RiF5O;Bh%6xPmg=U{$~4`HnV0U28KP-@l0wQ5cCvZlSrEU1>TV$uaut7vacp;RYn z5#mV-rD_c%gIZxIk}mC_ROgbOEd;(0_m&7y&j;Ci*gOzDIb=$;=1R3ClFgQwN{nLJ zjbV9lpj2ChQcY}0%=GPPy_PFgt4|B3gchLNB@46=l;w$HUw71_)IYZCzgc`PG*Dv zClq2KrZ4Ic|HvLh?2b#XEGx$UitM!P)Ym4_>m?M>Ea~$|@TXuuRXvQ5+iz;=6t@^o zk>qkD71{$Wz+5GMyVzhGk4CAc-su`^Mf3p+q&Mb-_KR;ZF{mMQ~( zXTSJ7kvhyC%wutK(a`TQMhskQiOKOi8_->^pu6k{x*KhaQYlXBhc(aSyy*E@R$`z* ztl*LccLy50FLAwpZ{oDLD{(__XVOuU!2l!K5={KQPgrnDvQxVKY>5J3iRperh!A+H z;+?RwAl~b5jC(nkd@Xy=RAwDEco}ekwwHvb-g` z$n3@C>X{y&4n5L?^=RaRe%~LP{V|#BjkDNw#a=vuHl7fc4vRk`attCo7x*R0+F-hW zvJxL+0LB023D+ngpV*RN60ytNTy{yGZtum^J{i!lA|+3_nVpQSCD*u_fmEj(OX)yZ zT1p4uWSZ4_3n?8euqX?Hi=}iRzoZD^){q6EbuJAk9;9?wt_e+4oPA%`66rk=_fFPz zU)+PfPiEBh?MzIdCm19Qf%7a|pDObN{3!E;apPS%2;i-K08o*ILdz|6mC}W1J4D*h zD2D7))+KX_oiNfD1o9>85Qbchf~EXdmQ1vAn29{BDH8e?t0f3LCn3{i12zV%MwZ9L z$h`skK}Dhr+PIG)8g*cz?n;6nVVF<``o;>lkb;rM^&3VyUEH#m5VE>P_NH2}y9(8^ z!d2CTV1)@iR)+-Ih}XC`9{R#qs>`m%z%b*;I>eG14evpqc@BGH*bSl3lquxWd7#x` zbj=ME*t`I}QN4|e=k@bo5?%9&Th5EV?zgR5UIzc)F< zhEpLmG~1RYG0$kM`#I)V*n`37HZd^|#?{f7FLcbK2{=RS;6*VaFw1;|N2V(BEXQrqc$X`fUZD7JOG`bPCiY`9Yep&YWZF!Slrl#PV`jXQODQy;1K+vlqw>VPvH_- z{Gx)q;{uncOd^_g1S`xFBfC|baH0ybTWWZYZ+WtE40PKOZXb;;>}C)m z#S3osEQeP@Lz|4Z^J@}OqAX9_ZV5-nl;=h$P{WxAN;7+GNQphQ-y zW^x_~N~>oq1(3VP-oIB8oI5UxM^W}{swjP{SpRErH|ZsgOg{p`)$}8ivT^s9NuWDg zMo7je)}(8F`8>FAICCkHeW2rmw8WD3Z;vMS9?6_qUC=lc@M_%#sZi1MPk(R>M*ua5Q)5 z6y$)LlO)nyNL`$oSuVuKW;+DD%Qp+n3;3yLO+k0R;mFY}45rb2H7tAv!48jC%w2Eepn z4&FhlPDLwv?oDv(lZnz2fyg2YW46q*WFbv_VlJsg=bF9L)QEsuvsD981Pv{fQ{ad( zhPV3WTc(n5HJPt<%wtx5GV`@z>DsHKSE|yh@-AFxi_&Mqt`wOhI>(FEG^!qJ(qE}e zftnDhx#;zX_i2cXu?6r&4W81tjjaB<2nh>clX}Sd;JS#V!K@|xq85QVg`2dMchPLn zR}F^J%9ur!QxCxzYymS$!JiYFli}xRbA-V#pv@86w{~syi+r*6`t(|gio?_$#+Y%Q zg;wNXCKduHSnAD=?JGbYJ)}84c88FoFR^47ZZ0lb zkXubjWrh*!)(Re!qT3PBT?9Ye1#URf8zFYx3=X!;Gdb9bSvP}Yjhd?apz!#M&T~`; zT~8aS0{x=Z(y>;{gsW>~-O!{LneE}I@D$#&^~IvSb1k8s7B~20nghq01|W%|Rv3!7 zj~KF{=|0&#faYEdOkG(rkP~AFlxI@glr=Za4m1iQu>dgzcFQ3U!Lj%uU)Cm{$FuEz zDmGBe~L zEq)oyuqZ=SjK|@s2%>-|2s+B!Y9FBzikrF9x~1KZB{?GO93%%~LXx9sM@|W>EXl!o zoJ)=*-($%PCt?Xa{7e7BErHKfGc?Q)KoB8*Atba(GSC}5hziMrVvGyXWs281vxW9b zJQ==63#bnnWsoha;_z02ea{6gWy^xtsJ5(L&sa-?ow1p^4^b2+F?=jT9`En^&mo<*H(8x#L zh)GjhlTK`meuD2{@ZwO!q_o}EXX)(nS<;R8mGz}j_36>*C8t>y`Pk)TloMjgn*1F7 z$!sJW`>E5Aiaw;QVTwrOMDS?PQsedkP|u~)S^_)xXSQmMR-Dyo zRQZbxfu_|0b~?7ldC{wQ{shW3nD-Fg`DVkv3d@}qMQ&K`di@r^T+Zdu`^E5=amVFi zE5CvASMY#*8rk(|_I}dcAy%o)LhE;HnG9>4!Wc%3k;~cU^4MT69%REhQRB&40zZT) zU5nX{m-0`8{2xrhf^Ch|;5SQ)O~h~V)=)_e_)Qrtf-S$rZ?g9oGFF}~V*n$8PZ=j% z=63ma5JU>PzY{}?#+wEhTO|q-o(JMXp>sWc53d}=*r%ch_>|v*9e4TiO9&}&N0g7N zzqV=DEN5s*@evY(70(rpPYrKoUMBo^)+HUok`wszl zi2;7^Q|m>df{?v0C9_I~8UeWc*|bbmc;0UbR_FEE{sc;>w+c6XNzqt3UED$C43tKO zQW9+RX4=(dfATpiDG5X)!9V zki;OghA8r1{pDvq_TXT>3B;ABTH#YYI;(Rp*7N{3AOhMsQ!h|T6`>!{F`J+d3d+zmLr={T2o~;G z*ix9QKAzT65*&Cr%1=dHEQ}~xEa0OQm?v31Nb{#s|2z!7L|#T;OV>wV2Jk}TlX%ci z*u6l%F4=3~G@8!8E0DIjww$vsWdKCtL=b8Y6|I%h{eDZ}#IQIfI|*aspft7iW3F22ae%!h z2zx$=%!I311q(})rq+wBX&=Na3+sw!8zMt&L$e`fF1UGA!ysdFX`G@oP#e?=LlJG5 z*9fugta)^BN6jPmbGC{$2}#Hmb#y#$qNF(xCF6vj*@vJWAxmV`yziY5xYC?Vkkl;j zF&Rbq;XnP<#~XNw2oi&gv-|$LzxvnT`TAqe1VK_879`|xsqHjk?*hSc(01ZtWW3UH z5F_IzMk?(N#7NUu@tO=k&7nv$-6J!?IvL>6IsuYYGm-eHK?vgx_qM+0KJp*-T4NTg z(a~mtq&5&Fr#~pkYb;od-dNxH(2}Iqq`)~d5tW@$DgFeOS}G4MM6OdXuHXT5svB# zCD0NJ(5XA&yj1n4)XXSrK)NylYZk|XW^Hxr=sjCF#ObgQZ{EI(x5H3qDj_#tNl)>ePgS5QufXqw*3A1My@hjC*F=BYfFmxy#VCcC5Lq1yc#Oby|EA=ZBqm(Mge#hhdeWQeZmU;n)Q|8}N)*Ne6XV&a0>fHc zq&n@ci&Q{;K2+A7 z5Ar3G4*%bHn6Lc3W)HFdH$R@nCql?ZU9uKnOC-6w4gN+-T1;rd+WX>fOmxu}~A* za}~S)&S3Y?p=NM{Vzi<87x{m)PjQ9}^Dh*HuB;g{(jqcSUh!bB>$y`(aV|{%cepcg ztF@^_2((CP%Ak8hh@ojkBnxY1JNPH0EwZ?=G-tL-S&4J5wbtq@x%9epzA~8}ctydK zVaBfE&|Kyp6=CmgVa%W+p@Igcor2#iy?4pnVR)T*WnsqPR&(oi%x0&-t9&Mbl4u<3 zgCG}#Lh4#4TA44?3hNT|EP~LQ_F=d=V<~H%6{{4u*Lt@WU*U%H%$aFMN z6)G3GtWg``ACpHBRBmAkpz4k$2TLEu76n9pgTY##THYy;{wC-SLcYw1sg`#PU5$Kd z+jy-RMT+S(^4DxLD2@=|BMecKs5261u;iyVy(R%a;AG8y34T?VzA|LN6jqD+9SC%- zLM)B^yh|joND#Wk;%0LEn~!W9y}f@r`5D>is0{m7SOt1*yn@qZQ}LBNX!ZX1^zGkj z-L_IfDp)QYYkjKa_@E{dO+$q$$YN^q!jd*gaMD8jsgT-A3Ha%4B|LIxu4y9{W-0-X z+37O+^nhW*EEujdCY+lN5FzOE4I7Q+zby8ceS&Bk$s@5lUvU=%vOseP2qkfz`bhQ) zi;dlQ*N(o)Jb++)KBQc*uhZ-_vhzfe-E}B8uEhjUE=H=ZT)wMU4a=nX4$8&VvQ6bV z`Lgg=+hR=Si+U@#t!eg(*C+ez3hG{;JgHk;eo+SLAUL?K&Zx^{H%`O`NB#>$%95&U zghwkH;S)GAq9_tcY`JpagEr(b2eJK-)$ojHeTkrT61DmenM;UHXuulzRZCQwiuRa# zF>ka5A<{BgD3OBK<=_fs z><&s!d_ujw8F^1;ny|26HpypCxJ`o7y#Y?96JmJQ+Gq?a zE-b+Z3M3}S%|*;jj`RJRui zf`$-0R;&a~YZI6=E7o)780F#6C8oIH3XM#Xlc{N;!%`&Yi%f^PN}&fxwK{V2Nj7GH z$b4=Ryy{V534bD7fTqG`RO$|pYyrR`Cr;VuaZkX`PQv?^tYcNd#2<4taX2Qbg?23) z?ujJ0hk|^l5MqWF`eUhrO3URMoAV_o%GB1DPm{P%+Vu*tOkQ&mqMmenD0KUru49mq zuE_RhxMG+tK@B7qp4LaoAJm&ONMF_!U*{=Z@ddw}tWO`+ePSNMr@6%mBYjk_C!ZFVE+~{T%m)pl^-xU6lC;vB_X$pXDd{mq(7wd?|yhO;~=^ zLZj1W7Z5}d#5u&*GhTC&p4LTFBk$CbwiFJE_{F&Q;rFnDZ>cwvl<}R)Fc#MG9_l!o z3s{=u0+Lp90W%pF5HrCAyy!!07Tvn)*%mUCAQ^NhfptE75XC`ZMsZX(iYt6CmKdFw zjJy6opc4JRPGNGz)*){hV$NOojLYFRx@D$nwMq^H2#F*#IibHmipWC95`<+5I0UVY zqhqXqp)mHxGA#;k(t!mT7BdJu6rjrZqx?HRhkjA^B53ZO$ph?Th+>&InM#kxO|rM5U8#0tP-FFJ&)8Uk(lJSMY@tyP}bu8yQ3Ql;|P4mhrD zw6O~c5d`@i(^igwXorgSoHPcdCD;QlNEfYkYVA{_vVDr~Pl1i?Q&jH6K%_qQwGFyk z+b8u*_SXDNz{HFo0ZlP`1Dad{_O?}cn9eGRivTfvipMJhXED}|%*$q+R(%LcTap8i z2*A%YrZ0(;*KT`c<3GoD$qPvs)l`(3?2UBWW3X#4q0qT+gnV&1LTy&eYei}Sz?v}c zA+o-Ufx$fjBTKMl7q|8K!vAt?h@oUn7xf^EC3ou%T1e%w=)2NlT8;vSKimnpVw|?? z3W$(2sO(l@_%X!Tew?3au#9@@&v&DmU7|EK1U1`pjA{M(X_;!`QW!Z}vF#M_LQZ_Ksj}09L zC>YtV0r0gwm7eQ+6|Q3$*^b`}z{lhg@V(8!fE2|AH}W?J&=IoG!fa2Km+(8^B@2s9 zPMr{w!^B|I{b6-D!8)NA8+0&##v0n<0MSbW2Mtt#b^iKfmEKMBNrM%szC>mT z$uUzRA-A+rF5R#`y$mk+`Co+QzM)AIb?BaLg3IS`Qz?=X;Z8P)Qq#sbA%Uar$tD<; z#yJGRkoWt~PdYBs9wjA58OQn*+_v~2y+^FVrX*oYWZjV}#Kl5~WLttXd#xA-E)2O8 zC{^2y&^G27+Ivmaj%%+$udpp|YX2#HwccVg9_tR$ZVmJ`x!?Op7tMS&ikl7huM+m{H zT8b{W1WR?x%3XdZnO!7C=fBQ%44*iau$5ISdC@T~rHjF|gBT{_CXhpI{4&yS=?R=( z>}KbK>_}A9rlN=9a!B=|0c%Is3y=^PHq65&ru3WqFWxQ51$&UHqHUhZ=waKj!9*vM zX@%jZq6-T2$6^hEo~`$U7RgLNTA*L}0KiJ6Dbe2uTgoBWYuYfYmOos6aj!%)AgH~q zs9JgLEUhdc-;I`nlhLnZDxT}pI7d!KP1$u9*f{)CzPbU;C zDRBIMb37x@hGdB8HlUqeavE85TER}z8XPUWEC`R1hziKKQVW!x|He+Jp{DK7i~?|- z1W9C-8gxX9CiPG>=@2c^44|dTh9|^OeOJ^rbW-&5N8ZoZ=F=>WvRJIcb0PgYE(nop zL%o`%z<+&?F|rKe3W}@QlWfN9Rh1H{@RS$+mI^mAyiv?H=Fo*Ttp^-u@uhyzJXzP> zFWYBT#R$LhA|q6a<664bc&6DI}^R?WHCbeJOr|Yj5Q`ncNB%s@U(W7$c-R$}8i-FbW@I z;8j7S9EKWu1_)~aKwc&`ioizoma!ElPNNS9GmXO5GHeQ@)uHO=upBso?#<>3$Uahb z20E14Q_FW4HA!L>B595%OjQ#K(73Y-e4lp(dn)IceT!h|*yoEQ?PULZ)CAZ>K?;Cb zk0r#Yc-#8qEo1EbE+#w?uHGexr30?6p9ruDu9j>c?B*j*tLGmTmvy>}vNJp>c-Y%;o@Sv7sW-UG~Kk=#1(W0z3 zrj{!}c|v1mlV+UXE}ykaW*tX$>TQ_mp6vDXUHszxlG{?i>5tWC>t%tHS{2H71(fd` zpqxnRi=h0d6D`MEv#+2^ZD2~K6!SqFXg8e2r%txev5PYy=tLQCrz?|Et-d4%6eBWE zbI-(zO83;}Tm=^YT%9dW?f6_rqu^&$yJK`GX%m!pppXS&Z3wL;FLh=a2U*@`j$ni0yFQFt=)WCrmj|xB2tfz z#iLHW9B)nEw&w5Fm}NdqZ>zWl$J2srXtP+?mXb^fQHw*&) z!XDm4KE~o0AwqW?f)Wan+M2>W9_P+?%rR*0RhzBf=6T6GjVPy3cwmLFiNYi-KOZT|dLp1UomFv5y{=XsdnBgUe74l{P=3HYL4> zOsQjTh*+yx=(0R-!|>FPD=?`%*>fsnAc@*sAD%sDYlH0the1H6}fX`c5w5wq8LjTsIz!DB10Cop9xMKwp zOk0LHLe)93C4A6?-5X3OlIKK$G}Idh4vm4BVB7-Waf^wU0snT~g3zSn7Up5G-_B+C z1gDoYhMD|MAdOfTdt0TKMiXo}WDISn4F@a)oMz)>BL05y2`u0;#14wP?f+4u2EYo7 zR*VyK+Fhi`*H}PtvYz4{c{?wn8~OaTTKd=3tHg;P&czM!chFjxgEFv-(30F9=>++! zCbX5PG_-2~AIcIE=`Up|nA{yjeV`iRKiN`!j|>?tu9XSHF8=Z*$AFye79efoyd$3* z^BuuQ4W;NgA64T(Cc%*ZML0->v|mThl>D$3{Q`;OHCS2h3C{&z!^Kq&%R$C~*m+Q0c7$Nec2R(r;l> z2!edkkRTLW9}e1ubq-(p<6RNG_kL|1f!3;``7K&TAdsg0H;@PnJ z!L)65l1@o+p;aG}taS|6ed1sXf|J9N&lL+ty^0wEEzL7S$in@P2=>O18B)*x$)x>| z8B)&=>tRz5O&Cho)bsBaB?EK~1B)+qS-6*!5z>)D40x@5Y{>SknQfKdPx;mWFTwVE z(CQzngbElxVHRlDwyKd)A|+8-HmevazN%(JnT_5pS6zwUFT z9qM9+q@maJX+r)HF%AhddDmhKNj|2l+e4R`^;9YA&KN0MUUpE>_=TthjSf;wjM|D6 z6I%;rH1r;lNcgcqTeJb6UXo)zU_wo>g)5AcM+nmDdBK#Oz&49)I}l$x9`shYujG#m zyjM#cvH70w*3h7Qh0APo#$;rCEHw62E+y+F?_`7|yGu&yZ#M z5;JS%^{V}9X037~uM*n09#g{Z5qY&mt0AxUDp*DtqRBNyUhT7$yjm|@rf$!{TbR-=!dGaxfm-;d9ln5EVcYYP~FMiqqO9V zcI2`TfH|&2-v!v&@ydHQYA(-?N3Rxnsn3p z0dIRP=`TJAGRBD@Ef0`A1bAZ|uN+>84(wn7l7J-2`FW$*)DBoSA`!)hRO2I&W|paK zxth8oN&O9?Yw?I(jw`QP5H@rlnL~^c7|tOMN87nfy;V^O1}| z(P;P8669gR`g+6Zm8&I%cYoM-PXUzEh6CHzF9~tqVsOa?>(fl&)*10!{EYY@8LE?D z6B`QTfBQcA=cok+)ID1jNVm}O7EDXx+`MYbPA_&UWa4~>f!!B&F61{nCPwq}4tx71 zqTK#lptt?O+tzLpPCOT?sBUF#iu@J;aL<6^3#FxlqAV>PKoukqG0no#(b|#)fl5n< z*pR1^4jCrNG&mSCbB?{aVCH1#G_gx)m&R7}1s@kXjht&HxLdBgIs%er1ufQ36tw&i zqpw&&lM7oxoP?V>^ny3mJVq<%=tt<*{ez;dPQqP3eco3B5VeRD41-4Zqqw&EEXfWA zY4!M+5)ro54tTX&?VJcsut^Xd!9d7AbhqpmEVZzx01biX??+FMFWA$rT2l893Wl~# z$ej=1v==^4$V-=6j*G*d`T4ub^`e>a%dmVB=HT9coObp%;9F?nZCFjgMIg zNE92JlE{ZE&xQmRt4qRmrsxvx$N`WTLRSs8Gi?~An0Cre?2=!Ai0`jF9AG=*Qo}y9 zTzU-Kmm(?_+QD?!_hAQ9i5H|3`~Mxeb?MG}JgUQikuKQz`Eny*oxfdNE{d8q z`Xe%{!Gb4&dQafANMLrbPx6tA89ctWD>8KGAj-io9JGz?YN>~=74gMH?NI+YHmpj2qQ>#fkq!iwN1)n zdqw;RzKuQd-G52i6XHck-Mb_XH7otf_q=Csl=t$TT)OY&!I!1!?BrX?B?+Jg>bT#; zX5dc2O5{NI4`P!%j9RNH4Hnuk#~YR4WCOXu_iucROqpqwDb9{>*ogq>BeP*t4sFa= zTjJ~d^Y3Le+75({Faf9p6}u52g5w3}|00hN8A2p=vp)MGbB`|$K~P~;F9uSW304Lu z@s%qw0(uzZGHQBBd}D@LL*e~~BDK}=Oj~@Pwm6Fs>}bOv+h7}`Q5A$Lz`kl}Q^0m( z)W1a)TVQ{oNS-6}bRgbkF_@C}=4wkD5wr;mmitL?(Z&vw5KYzvY_O`A6FO+v;s>@H zd0mG=7~GvZ4Ff#X*P{rE4vtc6Tj4lGXeyJ?V%CeG=T^eocA>oWi5atzd?Ma0R1+t1st*4!?|gSxG5%qm(mM zA!CRg?YPMOYE_OlzL#^yY!hd<3x#w511{F2lddo@A7x~?x#)CLhnkDu*4DsG06$Qq zcvFX|30scsUy2Ex46p)jIZ}7qcq3*9-dc(S7*9q~Za$Rl7bc%D&hNW(ud?@i_g$$! zFZGqLeN20tusJS1U(8DA#=rZ@mp}gXC%^HDZ=;a;vP$5L^L=yF)&?jYVxLSaueXiz z!w1`F%v$?b7Z?{C120YLDI;TS`on**#-pCT{q)b@^$(AH@YCA(x8|D!#rf}RoF`Yu znIF(2?VnYSGye-cvhkEde1=EJnV}&jS@}UT8ot&Zi?T=z6?Y$r+e^a_o*==yzn!#K z`IA0#G`J02y`Y{BqXqDS+##zop|H3WWQ~S=s zCLZg65e6fRAN>i_{pS2l!xV#bV*Ot#C=(9azaPi-g;`okRN_S^ ze<>0|Mae6t4h1H<;+}BQh%v6_iEPmVxHIJON~MrgL2r^uCDQPF7#BDju4i}Gy|wHrdkBJ1!kg>F%0qw zK}97J?a}%6$ddkTG{2Z&l@*eps?DKgO>LZ=!s4Wj|I=u;MqtCB7B+Q8xt*9%Qaj~1 z%VC7ytr?{?`tO$iCc^B$oBo?nk^gS^Z=|CC*1kT}hoSY`hH0SlDVhnQHKye=l5~p} zNxg_Hp#@rTH6~uLh6Fnsdu)9NG1P8Bog`fw+Vi-jD()Hj%c}B}+Pcixsry1pv+S-+(BN&)5hKn(&5gufN2PR&y{oP{t$+p|Vzg!weISfOI zeiJ5NsoM#-iVK#%D`4#zhi|KMcq!nK3=rb>U(n=PqCPnfI*a6ID5zxXQmLC0AB^ML zv5gaPu5rq%EXT>$)Rl2+CsG@ywztx7jU+;VB@9`oNFwdr%6O5neK?7m8XF6Yf+0f2 z#a9VB4;F^aHu1o4RT+-D9_#BgnLK7m1`^X)fHRj8biVVGMKQb(L$2G{lDr82(4#Fk z8cH;IKwR`)_7jd6lzA7rVr2&_U9zq))l=BL59$V~TDFlC*8f^XgS0^o>Iu8X2EYVx z9S2S|9T(1lf}k*7iW`I}__PG{fX0r5Pl2+6tQ=hqpRj9TGYonNWka}1s_F5wnnmU}P@VF-wX1 zWXG9&uFGbsy3W;p9Qh$t^@f`$G<#k&ZokHEVaGOpjoGipE#>PD-iwQ@Rv+<1-&&|* z$VAWiMer6e#F#x#Kmv))H!ST))v6tJil+BkM|PCRPytTyW?YrN%b^ux^(IcbC2!uu zf#@2pjvr=X4it^2pKg1j4-mpxIv8RY80lP$j+2AXX;e$KzU8`^AYRn|srmkik(snM z;R!7U7cL36{5~ER0Y)q@AF8%AVWoU%rtO?=Efuq5|Qy0 zS+bGffT9DJ@#J7+Zkd7!^8v=k~nTekEe0u;U%m|pqhRuh8t4ybDj zGByg+mh=XZXve}Nf2eFFG)pZT4{%?2NSO^zM+>tX1Yji&u8aHyQ z(74fjg_Mk3E2L&Le|AZFf#xGYe{q_(N`x=x@j&_8Iz15{s0`G2pfVBTfx$$@12E@H z;sLR%;Z-IoAm#(3A0!WK90m=f+(ziq)qf=aICbnOk}RLeoabG5l%DA znZ41xVi~-SwDf?u@KAxgT(dzT#I2mH>N>L@?a3EEN7Ri_V4tBwbj zEBMac&;U&W5Kaw;hF1qj_Ls(azK4G|*dWE{K3p9v$T%sZfoqry{NlmDHI-uE?CCX! z0PX&oqSoSh4LtUN6Z4-x@N!i^1Y z-;jLYr>SGS2j#NRY+f8J1ytJKm`Zwgv~>31p`q* zv84?Xyna>ZZ*bj%FQctL`MiV!CB#v-Q>s4EDh`MKP;4v59owOUIlJnsKKqed zJH>9~*pfF{@n;B$o`$?S$Q4zB#W&NN*qHQQnU>=##u={2gd_hkFg`ZnGo|uW4L=x- zhs5S%JCK7TM(L7c?Z!^mvZ&7J&>2J&0p#qP8D9Ao@}}5*^Jj+7{@FHs+dq3#=d?k6 z@3)@!wTM{2BEzci8j$7p~#)9dy zF>AA2pRq3ES9GY@P= z!n#va>%n$r!Rd4{-fzo1w9fMO|KXa}? zge6mh1TDNe`ZjRN|Hca1In?q_G^}D#C$z&TA$K+fS2xf3sI?0GtlySRib@nN@;2_* zxD66v^2gMM?fYpsvk@N*BYs*8$BEAjVVQtr=wR#bT6VH*PF!6uNG>4L&G*JLtOD7p z31H&5O3r-JM4g%aIB*zPC&Zh;$DDYY?Fl6~Epf_GWwL)GJ2;aQ=c;b7LVXHD=-3dJ za(AlT4GrsbAJ*v%OF03bhXS-(`?#*0JocEb7>1WNk+otB}%@f52)eU%Ke1 zZ62<8WPN%qPqD0|QuTqKwniI(ZpkLTm|qI#AXt`)=^T#Zn|g89*@PU#W;w-D0<_OX zSXm7m4_sdzeF`W}vERCpu2vdc9`xJe^#%);xE2BF;Xq123);nzAT7y14~G|gDP+@{ z6T)L=^tePA@zdNNO;qo~4tuW9%411MjQsk%&L86XIHyr*YX}(fO&#$!^j0urCY;gv znHwiL)T@@AX@`3mZz7!rlz!%eh!ysISpA@#8eNY6EOp?6SEwN{|HintTC$IKixQBX zV8q^YVLX8;k83<^fzZ*ih|egj_a0XZ?BX--ZlYlFgp0`IYC(WEfCg!JT$Ne1aPA-J z{_g@7AYY;D!I=GP40tXk2LP^f{tYrE2)z|xh@&Z?({UYbRJ8Sz0q~OAw1SZB>57m& zRabghsw=(pbv+RGPOpC9GQS(#xJlFW-wF`19mVN zHMWc5?PxGsztN8dnL9iME6 z3$ds_B6#~rrztTz(!1EmOBPIdJQ%7<@=3>o(eY-tDXyYh{3Qt;%EI~Kc(8Qnc(4Yn z3R(JTr&e;732-ctj$(mED($|xm%4Nc$AejEwW-(usq(5z|#3?xz^i0 zDRs?p&BI1sjZ z=4bl(uJr|m749icD+O_q*XekYk`J^o>Yau3OnyR)kUwrBd}mUG&n3cB9dp6?9B1~< zYG;7Da??}HcuGzzCs%oLdOLvdJp`8f1I_@}M| zl3~#E&2T27NoO1SX<*ye=qNC~UTch7$8_MPH>OeW|5<4e9AU=#WwX{_Xi_{r>H=g` z91aBR9GXIau!RE0=b3Kdr3c@YE(um}>-q0orHQRj>n|kAa|H_|#^>=>rzp`J$ zPe^=>72>0Epz>)vHSu9h(8w#ikt6~jCoR}W5FaNMC!0zJ!B|?X%ubSdkNDtxB?J-2 z7Xci@fN;(;10Szv$*~n8L6Z4G(*(k8X1RYZk)UQuHI?^6=@x3X4Qe}U~{Sw zv38JO!hzeLFgCWQPBYPfL0utI5Dno_yCi?gL`qd21X=N95Gnhts)>|+VPN)A*yF0m zk?V*@^{s<{thG|E5Tl`c(knD>(kp3I2{UDpJ(|g4Q5F%IxBBhQt4QlT|={H zDjNA>!189XEMKgkLsf!0`yo6*bVRmB$1)8%!q;c}{sQNjTSMVtpc*@D*H*j=rsN27 z@%jWTspL@eKpE2>wa*bR47@aeRsxx@{;hpoT@D&hfaNi8~*kEI}R zD;PrDAb|pZ3r3%#8$Us;K>jO?=yD;CbFm6?*seaPfXJB2UR@CEwx^qYf z;(@>+beSPW=z=qW&WAPn0v@Zg)Xt{au|9dgn%Z4~O8?BlRR=Epv+T&ERnmI)d{xGi zNJpm)beH;bZN*9LJSQ?*Dw#JqTKrH>;*J8tiz}=IHSx+y;D=MMD}9)hz}|WlB=brQ zgs5ww)RwwZ)2YUrIyzj4_rpTqwGsla5^Vg;k;UH%G>gAQN)mKppsOjCz3TNi99+#9 z;JP}i6(D}bG85-nDvF#ir6F$<@+A~iFRj>MhRTcuo9YEZeuiEYBf><~`E!9dtZ$+Y z&uR>y`Jj!(4$k^E()!5Ql6Qa5LT^(v>K<1SkWzrk`Gw$(65`A74aOKJdB+{3xP3^~#A% z%4QN4UMwPHx%aSJ6dLh!nfPb~94Xuh5SDDpx^`OFP-uwQi{eG-o0XgJt96>#ASSX@ zu||)BoW2R4IV0|Q^X#k?^SDKsith9Qi6Sr@7N`hJ`iRg!c~4e1BG{PSvttKrBX}H2 zlnneXeGp-XCKY+c0#r4$E$SdsAVjr!wHK65MFC-v(YO4BekGo5O}Ks0GETGvV+jOU zM54hwlrasTG1BInCE24)1?*dd?wjQ#)Oh1TJOlk$B^GB5N;*-Wi~Kq6D)@qD!kJhi zI~K*yOC)F_K|8u)_ z8a&9@nRl$}s2<8{#WS_7VP&_<@r9S|9acAid)v{}ZP&#F8L%vRFf&t;`f#RdI%a!) z)3g_HqcxQkYRp#%#t}PFHLxBKY83viHQ)!TTCHH^yT9zjbgcj}nOx)1>Z?3J_7?X; z*qc@Kq^Ld2JR%4&v?kl6e9b3bWj=FdLB02wK`|wFj>N3r~n*h~F#|UI}eH0-(iELw!H~b5Nh;qRHef z$j-99g7+C=;0w4Q-DyUrFP_hZW9K-#N}~Q8F7V=7+2^NF)wstE;k62?UqrBxgET!} zOe2aH(}<)MjTl4PLa{S6r#Q3~@wE&RL%Q1{?5hShv)vzDB4M{df`W4rBq-Rn?5M>V z@d*;95A@P{Kds@KjSdc7U(F2{f8fj-}pnF^<4?#G#nYXz*TqD5i8R9;2_WjI=NFNZF-wCRu+u zQlkaKJ+l0Oy<~L(V66ek2$mOcY#4#uS^|VOb5Ki3*fMZ@TVN=dUL5cCxWVQPRdZZZ z!{Qc*r?*L`2Ve(dT##l0Zj1kGo9N)+U)dVrc+&1BcqxlF&C00oZz*v$HV}pmnO?TV zzo?h>oIxI#@Jx6XE8*@_)xg|J%V$#WV(VIv^435i(9;$Gb+7>%v)$m9ZGZVA%ulG0ub<*EO z%CaE6Gw##?f+f`$NHb(ecfGSd5XLxbbk|F8MLZ9geS4JeJSvM;7p;S%pB&7YK4KWj z`2s9v4$A2Z-_n_;*%`a4GNA0TZDA8TYa)DuN1^;aTRVo7SXOTE`$N8#tmbz6Eq1$2 z1jDE|s+c-oh~Lx((i4!-xTS4}zQad*Lw$?NyboXN0TOz2ueXgf@3@xK*!6`UryC8` z$9*m8laVo*P@m{;QQzFryMSAgzsr`--7&)LxjTMcbSJZrkvgaAN{*)B!iwpwTv$fk z&V>VzZ{&h@yp9VDe=V2Ow3eI58NZ4PtFUk3vdXeNr9dy|7wfeodS<7uOD^TYBJRap zI2ic?F8K4)Tv!p7zGi6_nIY7`z^c=n_BS2$WTZ^mu`{8=S4Zz?L|vp^ z5-z3)s?A5%-8goJL^gW=VT0wmL@W4$*8{pDard~BYh?8~;m;y_N08MKw1iX^Jpllx zII~-Y-pXuz;+hqS)GFP!bGZGy&Sl~eMJ0VL+r{kIrrxO(Y~t`CD-CnIV5d}J zKQL_PU<3BA+0O@k4WU@Nejx6*s!P|-;DzxLm9qT=7C1DQyd4(;A3;lR^pE@(RCj-~;ga z$hOhFLS@C`YInF*t650+?ql)bt-kDz@T)p)-%^ax4ZSBx0d^QcTjB7FZmK$~+G({( zG?mG#266A|b9qHJlu}}MKQ&Z93Vg&@T{V#E%VHpHjRKU(=aW9`wDVC^qda99hYN|n zVd`=iStl=6*yG0ogjMDOePEe7ui_)QC0_FJKpoe+J=NM1cvtps$-BI>wVwDn_o% z0IUy1nL?D#v_7x3LOswB)yNN26$zGXKo-n)b60{lRw@;YXAc_%EmIz#-q}R zqxPJriC46?&*Iwwld!4wyVK%?^CDP>4&MvO8=#UP0g9_0M{YD5bH@}m19d1mCwpem z=$x7&)Y#Et-$;ZjljeV;B%LJ$O85>`WR>QBuO}VtgV1DNzCnYwG^*kISOX!ZhfK1D z>JhO%Hli6cRF@=(D-B_Z3^ioI!77gRt*Ijn%L82NBS?^5jqY*KbyP$ZYxxeD0!jCO z6St#pEqf=6^H_H5_{x3>Z)1khr#mnt3n0*0ZFmxxB_4pVgGA%&~^_jPD+N|KI)ndw%6J|H#HSFDj(HF_Y1i#vB{-8+-Xt z^5k)$<1;=A+@?>yGfCfwmyE~6aP>2roz1V zg}Z><*fGg=Yp%NClhJ)>u8{3zb1y&b?or#vEKZJ6I-uo4Y5B!^Wq)*0&ETJQqr6y_ zY0SC;RzyCc*jw1pCI7?Sj7ZWz-Wtv|&KCy4_-KR7Py0{iC4ySEFwqPaT(Kdr7jj~1 zw#lfi+4y$jddXLTe?pTOmI`de);@S6ho4pyAg4rWXtOuDz7W8iSM78mMB%MfECdbh0kC1-$z zCJlS>X~2~>_rYF*8i4I3D3IueN!o6z|FDu3<3!(u?MZEqj_$W)d|@qRQTMlWtK8<; zZ|4-5@mu9vVSruJAJ^sD?&<+Dhwt?I06V404NLQfXdzW+S&qu?Tzuj`85@-=~1dkmPBr8frMoPPl~qao{fZx`v+6kD0o7STLBD>fF~7z z72tVPonkv5;97hEx?j%@lJ!g9%Kj}srF5V~?B7NJDQwGez~&}WaxjyL!ZsXga+(W3 z?Ea|MeSF69xl+U|b7ZB1iIMcAWq$yXVWl%~r2_JNh0j}wV|XO|?Zu)82SC5$iKrbd z6v$s;bz+1BeW_`MZM+w}4r?($a;dlIIY`FnT3wDpE^FiwAGsMrwMM*_hly$=qQ?OT z1c-3Ty(dc=WRXEL)+a8KKv0DzegPN3T~xu$lJ@QI|m5Rw6i`vNQ>T+@;K6Gd=pRz`jeda+Nem1fTBEbgiU&mb(94hT5*%l! z4ZeB9Y_V_NtnBV&J#c0y@J;?d-wS1&$q&ZgJH zhuzxvX?_pQKdZS=M!;T&kmVQGz2VANeQ8cYt}om|=-jeGp)ozptu5@D{MJg1o+^cx zWg2d^uCrg&C6cuD5>{n0)05;y==EYL9oAwa+d*5nWp2I zNq6PXqQZmLmcq^tR(~HTf8>0bPBrwfzKgHT*!qWC8#00^OV$LlHRlCa#HCU?WI&A?A_oa7vS5b5G*XzPV?D%7*W1+JCL4 z*mFkfTF3D(zMHm9T}+0VwJI;nu!{zcs0ncTCSeRb6a8h{k5^41cbKc?{Vnr5(s@FP z2X#%DY3mu6M7GkYif|8hD`R5-7)l}@BLQu*#68B{_hWidWqN#^1qIPPxrJr-l0w7= zkxFZkT-_g+z@CdKwA4n&uN(Q{A7%67DlP9Rg6_KadAQ0#Ia z!}o9tu;a45fFjpY;dhVmLb zk!mIvijMQA?uLJVnAh~5&)vh17x-hFKNWj-m~x%BG1M-0;qy)0_f{P*d0WBplDrO% zm()>kylimtZtzd!-7M;ORduHW<=N5=bNE(04-68`aNv1phWoI91Z|rw_j}AbJdnr{ zvX84ex*kech~s+L+_5$3^GWYi+S;!c%}=u32Jw^35!-K$7?!2LDXt&o);A^S2_2Ax zp6V3Yr~US1ZV&jad0bD$=H@-*#-S}|pQ4_22Tu&XFi$L!#W59~>z=>{^GKwO-F=WR zhFbT$-BB!Q^Nbji#xMXwAkhPrCya$M72p-1GWQFjTZOC|uB z9V@3*Ihk0G<%f_4b6KFGW7^b82^BD8Nx?KXgTJ)b{dbx=pF&yki#a-4#&vVf)}_60 zz5DN+2)wAX**?g+uJ!1$2|Nrc%f-gSB6*3?%LDIEgirb>9^_L7Yadx@OY%VYH4b5A z$5CA#uq6xzmlwxh?w1~g@!%@kCy!J;n~|bV=y9?yqzPd?$6F?ZQ5VM&No$@Yg#o1! z=9Zan#%}!cHk#tZZPZHz21KNo3W%vK&$7c$h}avh%Kmh)CIP00Q>1=*Ck- zi1d5EMK>(!5fG(JH5^N8xG79F@-NB!QBpXoNg4`Z%1DCApVspjCpB&hUVJ}g6_RQN zVcaT^f>f=*Q12u8IDK328N)zkm#pE&N5>mqdOKyHb) zB)8lOITpc14FuO{+hpvJa1r7?s3$TvQ0Ra%00Nra+eQEp4vaPlT-x)+@U3i55H>4u z<*DL;aN32SX5%}D8_6-ilH@MXS5$>S^k7Mlhd2kcP!W5>BNK68N6YFHyg4YPl#faY zN1c&b&IH6urCf}AkueUC`rM|~$ zg4h=&&l|4I1+hFlY-!XY)F3V9e!M*Ur_7qQ;UP6h+2|1>W+uFW;KXTi zoWNp?Uwkhj<_w3bM?0kAEmW2NAjp#N9vD6bb!t?ZodopU&>!WF-5_Kfw~xy>W>c#o zBVE3gXWZD9FgpIn<-$Hp01>9!G^IuR6znIhms_|;(cXeN^Ib%8>^vn8{t!!%38DB7 z9HF?4ekuqAMO3Oi#FJC~rtQlIc~KELgi<$K3_aDMiur8u}S6sU1 z1X||%=l8`G2mLl`QfoVx>23VM9;bufCQL+X#;S~>EG)U{kvA=}Laoqy1^reGqR zD=FmgBhqS0AICF-0`Re}kXH1qqvGyqdUBNllT8$u_Egr`Wr~P~12IIhks^HwTt2{t zvL?z0vU(N4Dj)bM^fb!O?@YnIrNr&i5|aG_R*HJuxIP)9Cgxl-RFxCXljnz{JJWVX z6O-tTYRI*o1z=iay)i!#w+GIocFNV?uf^z33eP1rV-zrjcVPlZ4QrlKNyNnxkEQX1 z($TvS{Qt<M2GBzU*^$b?!6*kAg9`KTh?#aQnrIRxzg2UaY?PK*rpDT9-s~4NHuU zVzUIa)<)R@(fxN&H8L~P>a z6-Zc_)wE?=vsV9bt+W$+yxh!MO#DI=M}Po9RZzhU0-TAU3Q-$D1U1bI^`a`{7BO{O zPitDWV+M6FM4jbI-l+D_KrV2pz2D_nf=WKKtzb>}NmEv!7o(sT{?) z!ND2eYnNQUQoF)9HEZQQZZ7`lM$wF$CqA_ir=~R*euRSC zo71^K+N}OT6H=107`^3nPG4BBS_@u^&<;vso}(mmwP`WB&dVNnTCaK5$yO>VvPZC* zV75gM84LgR7@%}3dkPB(xPzkn78fyo5lz;iUbfKHLGS1&oe}jxV4!mW zWnY+CKobm3uLtu`*7D#eJsVWbiRh3F`t>$Csou#gK+77+r5}XtnOHd@S_O(XW6i=7 zOV44?5~bDeZHXcztyFGHAeWS!Xpy(qU94UPBud&o zf0KUSnSYBdh`{fmCDy*lEh8v{OMTH7GFrQKA+yB`y(s(VmX3(ohF%m-xEgxVV2b*Xi{)djjfB^~XckY4XdEEj>-mMpY^ms@QhWS#OL z>XJo}A+!y9sVBN?r3>3i2`73$k!2HPuLs&;~HgNl${|oOB6eOXF17 z4XMT=y{PV`4Lm_toUIL=$lmg4X#%;Tvas{}^U?%T^J1yk6UnL*@Z0f64p}{B4V~jL zk)t+fYkz@*fG!_Q+MYfpX%q_9(m*#NtfLpYlVzR}Nt^(n=0|^C&66!9bPed4RKr0J z{(4{Pm8jpJtE-PH35=oHPeB>c($}vsDrfNPr%pZ~^%Fx;8b7RoY}Bf{P&kqM>2T2H zI(-aE)lUh%2qqU9ql?6jr>6ErJA2c3$=IQAT1C-Q`ATTFrlFt*%Jv_mL`tZ|aG_XC zE)*$>rRk0FAfhdT@?B&N7hE)olFvM3Hg{tj-Kf=#5T0ftiJQiZBVje z#gWizZPIX|vNNdAa1~vJm0*=MTt(LFNF@e!i5H4F4Hx5`I$E2JPgfDWcY7r~xf zms-c;&;UcojXI(#Ba4tIG90l};cbSYC^8l~3~P)+DdQLl2anF!iV*3#AUS$gj~jTv zWfDBtx$k7(4>jEx9Wmygh^7A&Vlki-fmYxEIrk(piUb~Yjw_qyGVvFiil+t5paBz5I5;N+}^!*V|C zULZN~??MwVsZ!Y-PE!BpLJ@3$LS@nAW|-Fc@=Amk1-3*y*c4`RAzMo0W%x_l0%p(y z53!w?=3WFCUFij+U7jGP4MhiXFI)V}kP~8QgAwz%F3l9b!DP)R9~15yd<=6b@E=)C z8M4sCDPy*@$)&bK{DlEzN~(bW_y!Jn)i4AnKTn2&#Ap^OQbc>IB!r^WD_yC zC1rn^_muGfc--I8vOgCvc)y4Uh$R>xc)w<}OuFG#TOYKHqC`-Tzdav9x%Wmt-lKlJ z0x^@NRFjEW6Ac`o0n;3T;*W`$Ci^ubW;bM7Vydd(x#oI^8P*IC)`-a3bFB9M z;@*%M-WAg|rDY32%kEwy^S&tOsHJGzqd;N8uOTJF`EGDEN@h>(p!_+;cj-(HW|A_Quy!VPA?oKz_Qi<>s!JSs%xvA z`Ki!L3>cQ$9FJSG=c7m+;_4ayz8IfFPa@IO;km)5W)r+`!T*^NfisDi5JD;CHBN2O zopK+j;`ddazEL#9gG*>ZDbL)TPfB?bWJqWblWN(M;oh+f1?W9X zxsDi#lGPp27x>Z%B$mvcj&ZLKnLQzuci0n5%j`)$w~_()i^@7}(SU$a`zz$mJe6LRNZ)L71Oj zaBtWL(NqdX9?6~WSC@C56T*?4K5P;unUTx;P* zzGwG%5lx{+7$yNlj#>@PyU!r>=6~+2cM}?Ik54U1(>hpHg9@6X_N$rq+X);MGN9@;N7aemMc>x_GA|rz`tNoJ`}mOVh%Qdw!b#!jsZZ+y8o=f}zZq?A zZ7>~&TkGZ4bRMAxJZ-J{@t2CbQZGtWS9nCyqs7rb>=~qVqci%@&M{aaP7KjT6h!9H zFZl)3E(RFKfbu&j$|aBcB{adcnpJEsmlWG;<}Xm?dQ~l_n4?onIWBvO;Ddfn!;&mPs?{lW37nJm!j{lL)uKU3|(Eh>F(!iuv_re~? z!!cnkozu%m%;-T;;-V;*{{ZROl|DMibMEh@glpXcL5UkXm}JH%)d(HO<`~ z9G!F~mF%tRK3YRGk=!Z4l@1ic50mbQO60F{ZNIvuw_}w$iNydTuU1HK2l%E}SdeK! zC4rz6x!1ly<42+Ta-y&BR0FTAwu!H(+u|1@pU*+3PMD&(5g54JeYI?aZoC>cVkyWh zBiuK)WEYSy0ZJo@Yvs z*gLUF!8Ozank50l`*rakab?xR@gT7?X$_3axvmL(kbZW{f9Lz?*0XAnu7dTvd8Bmj zqjQ=Qv+X=70zGV7Y5`EtT!FvLROTVtlv08O(3YqRmH={z8acw&^h~SX^!Jl`^J8p9 zrQYP7P%4J76^dF9_h>16KP3ozcriALJT8sorwxoNq@8C(FDIC+hsP1!4ee~!pPEC? z=}-UHyRtXsg{6-EbltmS>ranyoAjrrq&JWjdSdM*o{4)jZ`b%Y&B&%&(_iUMt({+N zl4_L=x?qvX3iqd6a(-GOh@M&uJD~12xIfubGv{Pc<@(fOWH-667}@@wU6e|db+kBE zTFNUSZQ1$J>3+ibkq2TuN!9p2Nf5|Km=KO&{b7-GszuhR_EvAz+!-ipcSK^Yz2&Jz z2m;wIOZ)C&Hi~a9hh{*Pmq*_mv?HpDkMXh7i~6WrTTV*V^gOh1-w*pi4BuaTYEcXc z{CIQm$;G>R$B`gV$`*~KRJbcMjqzQvblKiTNh`3NGZMdk4SzDjQYgXDyv9t6 z1JG!j1_|~}#gJVWg=+~8O#jtz{(K@#(Ku~xk<#Yo3G?|6O?l6xaDFTB3hcT(cn~2< zY3>-J;m6Kph{P9Au$KC{;RM}m@V6I+VpHA`UyNH6n>cb0y|HZ0iY|5<1zYeMIn6Q< zCqKm%?JhzxJtQ=67)g<_lD@1Oo?PJ5Ydgkrv2nZubPXCrV zDH75=SEmrPCQD_BHn zU6^uZ_|-Jx;Wa(5iK=N5q=%k;;THw);i!Ef)EP_#9sIKb+sQ=?H;F$02@3_DDbHTL zqk-q%z@Pmgr|+n5cynRN+w%chosVpW$f0H}{AC7%LC;buGl?PWRzP>}L))pZrEY>3 zVo%*RG=VUe;pApwucYnH)5>pJ8lsq2Cb8ysAm4=sG)KN)gnYN;`s6@upCcykY4zH4 z^fz7h``&l8g#ULJ?<#(SH|ycsi{rY2W?}^O0-5=B1$my(l`Yc=T_OE14(=-crtV`; z^Ml+bZWHDexQ!O3QR#d1(M^3l$}3y58dlK)Ys~K+8-TANG(DkPMe;IoIBw{h2JjG@ zWcY}ulNXx7!+gA*MU+&U#hyOcY{0`7I{^8Z0rDHX+yB+%kD<$%qM~=|dQgo*^^3DxY5% zo3=i^5VTb)fy`L%4X!QqH40al7XUkNP;X2{L?et>Q5M6Sb1@y$I$E<9BQ+1~(xiyX zO1UEvoWM!rCTgROQVx($ZAR=Mst|gPRxmZXeylQw(8PQlvf(NTPD2FSyp)6hTQ4Q0 z;ZM-(u*1avLX?P;IHajo=2t@&mD?FH5VXXiUrGmK_7kUbe9A?bOYcHtRPqfryVfXp zX(~%S4EXL$rSEa7JJZk6K|V5-Do-tH!0NY5pC)Sh`6&*FFllN;H{v#nr&_{6-&$a( zRwCDV%}FLTnMA{$rO}RdT{bcyIwDdmwtOa-i;^^o`#=^(aUW0^D(grHBT@8-Nb}yQ zguic$az%Z&JHcB2Hc-l1!0#A7h@V*5NDf`8x?2{+jz5{l-4fqF2(`Gnorv3W-|;cC&`{H&@6ju%$62`DI;*s@aO{?Z6LA`vZtJ0lL$X& zvX07G^o+AqG(j}xuTEP@-4y3_JPiOu^aO7#dU_HOJ(+z=JqwvkNH@5k>5-{-&`(r z8F2pr?2-gxLJI^F{3!hXp`kw&K$!8%qG5T@YYs8H*VWEjE7(+3t9@RWl8BqdCCH@2 zB~gZ}iA%?ZNn9es62buDQUQHwMWuEoen&Gg?P=9h7;lzvBnHk%I806^xgFOGuq?|p66-``S!UYM zU$<-{hf-$}C)cl8^;BYEOTac;n7LtwvLi^Me4<*$9<`IuB0;87N01)tA)5+VLD~Gf z@RL)QTHHd2JKevlcTp=(K^~T~I^yHMaT&r^_l*~-yNX}u4T*-&>k2@R>x#%I*+7VM zR965g`9PxLMc(;t-G@B=z1((mK|gO&nUq2FyKMfWw1b-v3Zk@<9zsaQ7ZR2r z>5-Fz;bY5FQvO(DwB8CVTO*Rb)SRLd9hviU>QnRUjs@wgPh(#3+q|HGKcOoFw-y=z zy{O*+=t*4x&`DhZ$V=s2#gFJd075nx5I5NpPoRT+F6byCo3~JC`&t3*SxS=m?*g~U z{P&#d<&YirZGH`vVEfu-N+6dKMPB6Ac~qEhZXHIG4Tw*f&>WU6QNmI(Aez9@yh3(1 zOk6~%rF}i8UtBFwarKT6bhI#mq-iPd3Sa5Pn8rPfX%~_)tvC8xI(0#I0Mw>G)Z-4x zgaXWD`m;LvOE6DaYmg(h3#BV3KI6i^kFOPyGxitni~UqD_EIu1O2mgsYc~b+x87WW zTR$016rjN4q-T+PHJ^@fz6rBe@Jm7nOTmT|!z~5Oyu&3Ciy>phTNHOen7k|~ewF;O zf;5d(vfbR`yLy+5D2{Ra{Q4}v?%ZdOV=vI)%;t7zj@;R!#zFQo{Rxor9uqAu*}7GD zPBvJsXC(;$4D$DzGcsGK57|j|tTCtjg#ALsA0e@K&0xVsA4M{xHrZ;lsorv9konc@ z#sKmSE&GNw=XKv|TTmV-4@lr-lR$R$W)%fpjehIC-P_8ZO#s>+n@pTAE4D~Z?UPQw zQ#McL(io&T$k&+!*jGHVN>kl_;$i*w+LKKepxG~1Hj{}N=SI&^)Z*lbD(?l)BX+sH zry;KoqUlDSN2U{b8`}5&yx0zd=P$1%GtcNB^kQyK#o-&5SYIB(C8nfX>C8P+(I;RYH^LL?oQ)iX~D_58B_I_G<)&J zWf*Hp9R#|OIw;;1ZSqHmHl{LhUVbUOKKRIM%)N5nR;+dktW=2K|usU$Q^%0 zFtm!^$d1fw-TVpj3!jASyc7iMj(mcB+7dZ$o#g?r-$8@U^0X^jXge zYZF6cj)2W`SvSkLn~y5J)fK@6sE0lG80HRCaO&-OYr^Ri??*VHSI+T)w6q%%Vzp;s z64kW#Td5(;slB_Q(^XyUwiYCLwl^1+@K5lHg8!@XU^KBVUsCSyP1%S%Y$O z&?)v#n_^z(?6ZGS!$Bk`<|XX2g$$uv*k}J@=4z^HUTG4X-4;CpZ&WK_(7L;omsf6cgFTvlp@P~Z`MBRVNdO|-UhY)=V)Q%E6L7i zEBma*{(xyYD_0xD3v|TR+3d^xEhx-b5c`Y!4cE|#X|ep;HFaX%CZdbCPHd12vJ0(F z%&g2mtrH8Eu`q_)b1v-w|D32+#~>T67Fj7KFSlfn9WutO6k`m-MrV$?I&L{A#e6Cm zz8vU{^T=>>*NpBjl0i0hw&>e>Kuo9dK?YDU(3+o2n@KM7(!=B!oQ_t^boV0t5p1yq z_5*FgAge>wV6g~!p%p7;)m}18`*G5Wbu!U#vWS9T5cGSs@`H(jN&}{|-eorRh&5Mi zGpuZES@(q@TCsi|5Q1x?<`r~S#>2F%En?NfhbPvig;ostZcD9LZ?jem;bp>L!&Lgp zTCpS2t$pEIG23iRrKei4@C+B+F;!?ii+eOrTV9&eiV4ppH^~C}RFaz{h4n%!cCPP5 zE(@{#1_Y;CF)UotNlsM=b6T+@7MakB zbtsHQD28SV#B&K?{GFN@!Qq;NuFvW!sYdHv+9Lr?S|ycZ%a$vL_V`DnJ<@$>#XiHn z7HZRK#oC$pX?e_t?*&~M*Xf13ihrhigu#EIx(Bk|Pxd}Toz*O7jy7n5`;Jyj zlpLZHYP>BBcv8PLFCs;?+85+BEc$`cyk}cp5iUitd1cylZxF^An-_jvw1y;-<%^{0 zVp`i0+{vkfB$7)cgUIG}MD!mNKFPqdd@0pV1GEiw3AIxxQo zPjBfRZNQeB5>{DkO18pUZA!GVv?&qRc|}14x9)p8-iEfij2#3MW4GdS#>i#dQV5Co zg>V9M=sZiGx?FXQT)}tl=z3hO3;KJf2Gf1xs&QYlnG5h?=u$7gZ7}O_B~o(@hpS%j z_b^R$9jVE&C^vAPVJWKDBO zwcM?eHO-w;$Kqd2*Yhdmy>Pecb=|E1aqDj7y!4Gk04qJF?p7%%SXQxDaJRC68tzu$ zwM8s@%LaFbho?qaoTwzMf-q|qrcK?g>@_j<%b}eYUK5)+AC*LuB_+^I zl9D}%t|BQ(qL`JG^5*fWSnB2^( zC8CcDD4lWDVP?H#t^-W9ljvh=0iM?8KP$%sGiIXtE#W0eEqt+3^+t|WQeG>N*8GdS zf__uKiEsBxw}^|SgCYw8vGAtR`qRD4E4p0;Q5kE-Au6pJB9jM|AMcR4mYkSCnofk< z)$w6$7mndite1`s$EL9uXET4@5xjfGI2!^hW7EL9#@SfJm{*N?OjB0~I)h;rPqu8B zMI7E_m}Qc>Z(O>XVV1GkFw2UxVU}@8u$3$?W3COeu}8U@VYXd)uV9#EiOuP45|&t- zDNF%l1i3gNy`5YM!3RCpw)D$DtJO-e_B7q46-x>GDUYvepxyjJ7-#{24YUAYO9L%{ zI!VJb2HF5jYoPUv{Lwei5y`+8znD8yRX9r74Hn3PWvsa)u1G(ktnC7;2@E zqNHo7EhUvv@_uH_#z%$0p~|j!TQ*q^*CYWIVyQ0D(iF0?_EjDSG{_K}7<++nQ9*de zdOTalik5;yHQ-uhi52fzdeC%k(SKl4K`%2wmzn8nS!cUQq2M+2-cc3_70kd z)XPd6zOBOzofFN5ct$~p3+GedVNiIhyg~f%2f62R|1|fd4(E`O-}Bd8`CA)BbK~eX zek%4wCid?96TS8RX4wfaL(WHY^qKVy`vn&Tv;8T4?8}F$VXWt66w5&P9nwVWoD6*B zcB|l>QWJtp`zi%mGA9nJY}2M0a&0zD&7J1hHuc;g&}70n9}3A(vdez6ENBv?t7Mn{ zg}Fx`c)ycfQcc#)chVC60E^Nr@UD#bG1$~B4tpTSYcUxHl#?VfSMmi7vWW%MQQ z78Rbihu^)n7KfLJ;Rbi?Ys<#W}HssTix{@YMg4@)sHl!hHby+8pmNX7FgPNs> z8FUMl9A?mL#kGVmWf<2ogTh-{Gw3gW&qmRFRnXr_VKZo$;kstfkhr^M&~p|xw=jcR z6G>m@T(g0JS?E+jg-~S$mBe4mvKT#Qtc+;9uri7z(46&ClEYq|QB)I}{VMdq5Cj=6 zqCq;>#-HT@;<%QqmVls0(CUKBjz`30Wf)8k=FE=K;5*dtoT(8gW)+_Fw$uQMZAq9K zfnqMvzUs`vRZNY~f9yI;jsKRsmyh*646EE&?1Y3~5i#48QtzbpOG>Hdh|u+`MTCYu zao*P0u`+H9L-Ynlj`=*5gLD}0NtbL)OEkulG6%C-bbpTf6WlWy!-xVsJB%pM^TUV& zeP$R@pcjUYF1(ndK#%G$Lf*U11ba6p&B<2A##^*KFgO~TAN>V2doqj@4hAh&c^eGn z(~DvMQsu?bUw}}yRYl_2@I7f)Yqz$TfQ*u~!tLnL29R{VK3yJ=#x7|lu`JGOCMimT5wBZJ z!JvOx1G<0AlxpkX9lti3l5U1mdtYcKBLs75Z2UIFI62_|EsT!K)w~0Km<8vQk=Hgh z4g=V-2j-7&o%X88+DHk7NgFvxyE&2Hb(!0?Ha1G+$&I71*Nk#5YwTXHVr-UW`A)QC;EHMgvqViEO<|V| zh>5i;{t>@_Pc}NPOXz4Id14nwA>K@KbH<&c^(0jx>Em-d^aeYP&GZJXeI8RXJziy4 z9*lPV&F5&k>yR&D{^XDWhhzS4b1T}z82b-%3uyBUV8gOjFr<;@XE8~t^4}h|qDS*G zWCkt>wN}y#jpVS6Mo)K2j=JQTPRVhXJliP=pQ7hGB{)IS8XDKO<tUS#7A#Vh#QGPzwlOm_QS~7ZGp%wcxP7n4{3o^J=E=n<(@}d#z?GC=FO+@b9@o zN~aKh(hz=0#&|ZbQ(#c+BrO^4XI(%Rms}u!LhVo60vXJmh&k7I#sy?z&IQl9piS$7 zG1ZDg4TF7wYCR#K{0*vwR)Gl@$d0Q5AUmNepggK8pgpE5pnJZ5SMhz^3!F!EWp^5^ zr#WIMh|uW_Dl;S5#{g<@A4oIziS3A~mCg4FR9z5Vu&(ck4v4&_o|7?Cd2Hyo3og1K zK;(i;E(j30;Ia$$PG7m=f&h_gtj~ieG>V5cQiG^EUp309CrVNz?V+UwHtCsbG%?-- zqK`SW5mgamI|sg}yj4Ez!jz#cA@2lrI?_^>jV=Rb3B1R+W;3sIkbz_qt;!=aX5P!R zGd1&`FjS8@IZKvfoykPgr&cwc%G8CK_m-9LqVh~^!goPoYkXgoJ~aqgYv#=?7C6O@ z3`@GryrM9a$&{Z1>>F+K?{@bPvSL`_&^e%AZcOp^(yVs zub?8b`kIb#6|SpoD1@+aFD(Zb&g2B4Q=ZKhu)OYDS`vsZAB`w__HL4{>Zu4a7Q+_Y!`}J_FI*9Um=XEbKxElsJ1IrK0NDynbZ&dcIEhx$ zMMDGrShV_M(F)-eTK1(YT0t!TShPx1`IST~VD-nM)vFS%#ET_9T}#B;O0Mb!`zeBs z{t=d|3M{LNF3ob4ZEa}DGRs&BcC^SqlU${mD`U)QD-szcN@B9B$u^;bHp}LhQ%ou1 zOlIRgQLJo`DJ6DU)QGqm#L2eg2@7k;lWmDk5p(ouTXNbZNR(~KS(hMEwk1!w1evnk z_8~+ZMpN@NTNnv<=gIqenEU3xN@UA@R1y=3d_n{n5+#GbtSd73y53~%UD0n^dm01j zf99^Et@?QjHZ$8736`70x#xLpYjKX|kO$c?vVC!I?!T6zi_-thb$0t=k@c|PJhOe# zaFT3p7@u`Pcl+Xk?)Jq6A)EV^XI#+TzPMoX_C=6L+ZO@iZ%}Qq3rc5S)V4(xwQbQ= z+ZtW9tI*s06_q7L-AuV`d2^uad2Umn>jl-DX3CXY0F%=R zbiG804;1(IHwU_2q;w|G^{n>dktz4IZVw;cfIe6drg>$`Jtv_URF-Z9<-J8@946CM zjH+ROAmX5XhwZ4B`AQ#mdM6}8DSSC+jm6iCle92&hO$7xIyHY3ccgh_N!kdC;%!n< zvdIkgE(+_85fmlb2qP$GA(7~g%U(WPYZ>fdo5k;%r5fjD+b$>4N4d0$^Cr6c!$kL< zj)^WlMs>c@eAf?c=v2Df6z4OI5T!A#!s=b+fd;4e(O;QVe6ecerSM%FWTJJF+Q_1o zeiURSrGv~o{d<#W35n@s{@tyDw`l9qQ>%5yJ_ou66-oBFj=gS=R=@$BMIS>WEiZO2 zS=eNqL$@dE9EbYJI_F=|Ej9nHu+By7;akEwmrqI3Sw*zUETm?80k}o%p_oUpT94?q zQl#?SCM$|$Y9pefE%I@GdzA6Vkg-J$TxXY<%x*^%z7?lIvO)Tg;?rd==_srI+u!4f zdqDK~+uG)c=BTomennbu zVPm`EoqE@zcG)Ezi&}HZCAf7Py|q1t8XP0kP{Nk5L5v~m2w)=AFfE_j*q{m44%o&P z$^jG)38CEFWkoKg=Bymxm2248j4xnHCmVU!#HQ~VzeZoB4fn-)ZSBDDg1d|D##T~xgI za2n0WHzJzv^v5>lqxqzDo{i@FM|YR6du$*R+nI%qiA_EPVPQivlya^b6wne*>;n>a zZkch_b~HU+CncJEALuR?iJ4B@`cgLQvq6a(#{Gd1SGawT8oAsb@6z?9{`gj1j||4n zD|U1+-fheojK7?=;r0_SMXAW0q4HfLXNN~ok+XV`WJ%J(Ua10XLRyfBHETk0QEDs< zHRv&lWJlU#2842fuhf&3WIDQ4O4~S;hh`6!RSiaj_uT&CynL@nt)F_kcL!q3iDf^mN&BCE=Wzl1>MRWKgkd_7Ve-eGh zDT+wLDK%)(eEQpYq$ARIQ#$okr`_e$0xm&HixPmD{;Oen$)Ey?fT|kqRY^*E_Vld?EK1y{e#d_ZoiU&i5$-3qi{8 zsbL!)v0$A*9~BZZ)OtA`O%=U=pWp?W8a+W%&{fkg9_xClKWSSzPmgzMe~@+G7hHhu zJ{L%tzcZaGDMfs}(~ma10-o%TZ`Y?z_Q&|r?!`(W!~b6F5o1pGz&Wc z=(PlZjA4pwY1(;wOfAMdN_NaofQ4b?WECu7%;8$nk-Q^K`&#cdOH^A`#4VTp+?@JD zzuX@Vv^ENQQX|ABlO83kf$&HhpiQ!!$B)?QqRScaSX39Me9$?wXPAs2C6@9*D)Qa) zOhuL}G%d|#ozTH`gEcmlE-u5XrnZN5qa@ScLCI=^g?dFT(CSNWiOs^-0o72fqt17` zX+v(a%$bzJ4ZLYlhOm7FX*aZ<%hYAHeo|ILU?xW)MGVSONQZ#iaecxWBl=B_qNjD` zu#*j4>$Kc&XJ@G2H>QL#9L=r7hxM1Coza!^yPc1V3`Ohu{W>=1B0=o`uIS1xPL|60 z^gqJwn(JEiT=? zM!dGjnR(w4*6+0I$M`iJ*a?1dHfUJsJPf92T@yd;e&{SZ-JkC;kv^YSht*M1_o|~_ zujee()7{*mX2T_Pxh*;G61tsB)ai9PeFmCY?}6p$3SPaSD|mHLSMcgNUBRnobp@|3 z=n7t)(iOaNmM!q=q<(`}cJ>CZtbqWptdRh(j_EmgWsL=Rbwt0xtHZj2SL?chS1%D60vdIh-*H&Gt~e#*!$n?fejqCH7eO))sgc?KR`ZwU2)Dc9_L%B%Sa(=dPxvi1 zx1h> zy#r;nMW3~g9O03S2T0i$OAg82qm4BCBIjvM_YjIfr6HW^}|cM<#GC|8oQH^LK7#rljhmz z!wF$FeQg8B%UtgyJt-nmaj+QI8es3xq+)KL1xT=ac4WlwlnqcOaQNdg4^1QMZ_>SELpSxx`IhSFv|a8;a9m z9Q%u}#`B+xt*h)=hmjh@pRul(!)+_Y$$Fzf#W(S@rfX1aE7?eDnKEN>7h|_ausgJ42&D|Q!gf~}S2nc;N`9NWB`2o65k-hJjohQ~e3PK9LNm5Ic z>V+$Qp?xU14c&uCAQC^h_&t9*TIU1!uh$&6qpul(?xz{WM=+I=;;T0o%ED4-6$x%jK9^*>DC`(V8Hf&3nuC2uXMwdB(exJjF( z$UD+QvG1N{q|??$8@Ddz!8Y=DaloSU`zn73K;i7iXgU5tJEFx$NR**qs7mDcgIu68 zAK-#U<^ftnt=qi#H-P0Nn#bjtcF**n5|M|fYK|c>El7UsxzbU5jEOC6Jl4EQL-*e zJ=vf5vN+jR4{-1eVRN|#lNn?f(#v>W&T$tsw>E*!eOImQ85qwiM! zIsz2-mACtBrO-F-@fb_o;N!4&lOU0M*Ssl?Uy}MeVn*0kFW2q|dP7IMj4&FkX?N3k zVZNzNpK<`i?d_+ahrD7JSceuW92iCaFBc7%y+|K`8mvga)E`+kmbo6s9+A+tA=8&p?#WIWkfGM;HU}6B#nB=s zHGW_Pnk}UUXm(%qfR)yo!c4CGJ<*t>SSkcI6k7g!xW}ff!-OtgsCDh;25y;dn^%JrfTfaQ#ML% zk8;da=S^j;t{h)`i0G!Vj{Doc>Frk!qcfh9*E~nE?m^ zYEgh%mUzI37PSzzwLkVeSg*D}Fh3Ye(;#SIy*I9Fno>NOCU{Zw%*#Rs9DPD(A*xk0 zZJsJiAu140)MU2>EHK*9y5O-0BFzblSybEkB0cXlgqU$F$zQ&ii!KY|;vxo;YuY4~ zE7q>Kr~B>)T@;aIuC|im%~qlju_wL~$XzwhB(ygknfO-C)5g$=Moc}?->P{k$`F1j z6TY48O_0RFwZ#Tl8t)Ba%r=4okRQh#!=L!BJM?-R-HhOnFCGjEe7UD48{o2+PONlH zHWe}8Uugc|_QefT7{E1L- zA9*B=#F)CEpq$zuKmi<@5Lgzp*bToHZy~D&QCq;$H_UT^h{MzbtNnDLm4kf5w=ehL zp``Zhfi}H+b8256ywvlYgV221gp9)VQhM-yJ_0dFnej`hef#WLwS3ufi0^BOQYQLk z+);lr%2UF^ZcC8lDLHT`7ig021nV>B@pJ|2x!AIvL?S>{rf`--NC9+!{wu&2?Inq; z?_OJ~ZxQXqniCp&VCeu#%ukUjmN?QGWoT%U*-?;ipN+b399Ldxo48e0rr)rhnGc|Q zK?3o&&;`GjAJ?sL(wo*eNgaV+s6M#&$R|LS`e4nqH`Qb%MN$0;b=LLry6_c)3?~G z|IKgTDE38kI>)^{*gP@)?U&|S^BKu{pfi%^TFYl7`!VcE&SKyye^KL&P5=ZWiBat7 zglgw2a*k`b8^S@;(h~X`c$qLKp_Vvo1`#)h16F(Wa$v(uy`z3iy<<^tU#h2N7N{ES z;rsw{P)KYEH={R1J?Ca>rgIz|M(fcyz$}75%v_5En=62R*RBv~c8wNnw~>6p5J7M< zA?WyMeq8Pe`(Mq1bxQNjf}Qs5(k!*Ph^>&(`O~ok7$blYgZ1!VV*B!lUT0q`a-oIN zeg23Y5JW^`)xQvFRJ=Tm#knP^fG8cB^GE{@*%DE zwkz5P$TlU9m{d-QTU9$h4XG2HUIN>qH%T&o-#(O5Mxa)Cz@fP|(*`Sd1V zq3fw$IJR5_sj_Fww~VQ>zqmi?hOsg!lUjtMOwT5>FS6SU=MlC6wD1{vBW0+j(lkZN z(;OD`-m#y)CmP0NOsaKh(P3>5;a5rKBqoP6jT3H9#jQ*=hyW`6a&B3l6sqr>k~hm{CBe`?+f zCfdMFz~6{!k*_)VT^~5qw%O$TUk7NF1<}i%F!t%_*`VOJQhyjd??%M*IVIv^*3KqR zm^0Yt_4{-camk)h`@-}PGfMs;`V9|lTk7@WzrsTon}Y*>wxkkfmcGb$zj-p3vil=< zP(bO^DwKN+Hjk3RY9*g@ja2NG%lNNgD=sC(wCy$aE>)*hxUnc{$6;ozXmkL1Q>_4J z3r6}boIq5v);;y((8W63*oPR!8wQ;wklEC^wTAkN5@pW*tI@ALJXx}H6!WfS*4tL~ ztCFeQ!+XmU$Cn3&rHb1MqZpJ5vZItsuXbC6$0x0;xRFgM&-@&eLAJfz6El9_Gra z=iXg+pOj)=djh%dcv@IS*}W)DFauyx%r;`0TW)$9&hKM3^ePn=!@UR}9^<1-{PaiJ zwHF^vpzI4r0444PU^>SstlX^-c$2S$5Oq6t?cE0TwP3&+T;=~I9gGZ;$?+@NN^pF2 zZH13j&=PwoK|+kjG4q;{@f*}T9H`o_fnW7*^u`&l|NIB?e%%SlFJ!~V^m|yuN&3aXV*Vf#o^!(J>*q< zdi&%3s)sX^ottoG8r2IpKvm-#M8V$R4e=DHLL{*Y>I|6>IKZ-W5&^`t+FvCQ&GRm! z?OB?F#44vySO5=}UhC(fPAfalB`y8wqWpRK0OJi3qglNQqJII)P|I&eEWaH>ew-al zSm=7&aeN%m_EWp;MZAaX4Y;Sx@8_D;ZG<0+_yVw2D5ih;{f#MmLu=r?MR)4Jk+ zaZ1!l=J8sUmQ?xTN2BTv&bbRpIYY-$LR|mCJ8Q*S+|W z=(-a{rmm1l~=~`$`x+wxDDqI^)fkssDsVn!yCxw zPpOZBJ}CW40khndRL4Cl zT4c7X>z;JPI8C@{ozH1A3PR!g zwmZZzLKc3DWnpX#w0Os)!s!H=1x~TQnDZdVCD>XT3B%i{)5#CS9Ld2OC9hD4biZCKi1ggw-uRn}Mi4n6vh#;x?|KLTbi-%URr)^033902i?N%T6I0tIk~Ht0`S zT1&@f#ahQNXF4ZZ*}^hXL$*@fbSlv8N}{JNtss5O_m~Be&{vH2Xino7HOTEBsg6Kx zi<FJcSDABf1CYN$mh^TYql;3XvZ1MO2Qk)?hNOcf5Fqt?C5fNleTa&d zPngZT;a-ZlksFcb2Iyrac$cCHS){QJrF>CM?fl7Lc-~yS=x2n4(v)525znast^Aw70yPI6&q=8kCQ z>esPiUM~pNwSYPHEy!v<%=O810$%Xq!n{~miHxQ$;P>vm!3ETvv19@jpAY8;%Q*CW zU-@irf*5HqG0Lp&q{y-okc*f&HK>@lW33_)xamW!0B5*SfSX6WK~7S#h{WvK3OJ=vzI> zX%23cmTQZcxC*)^>bYf1oHcwf?)Kz1iB3Ms#W8VCT2xsj*pHaF=`k-pWGy8ugwy42 z?1*lJ(<&IAztCy}?WegLwG|SNn~o}Rv`fE@iOcPwmxRtRu;k{+LXVg@H-IAmoYZfn zu3TnO=iG_weGT04F>JMFV&eL0G{?j_cOo=T!VI+M3yq0eNi85i44T6q0sCj7>QYSH zAcs&nst$51;mhu}K&^l$k$*8_;;Ly(T)!z8lEFrZkNTVcL7h15*eR}Hcw5KBE%1p@ z^Z=#YGlVOx+%00_7C!%&I7_H6RS4_KsPQUd;*!@JSh!@WD95VpzU%Op%`tJqR^lT# zs!i*!!?v=q#9K`fMF-+_V&YOhMpQZmxQ%qzj)`OIev#<=6cfi40NP|SRbahROdKzD zV&aCQ`ItD+^-C=#ZoAgY;S~GE%9@XfLr;tEO~k}4)r0Y(1PhpE(qG_GVg1GUZ5Z3o zIMwyS7BO+2#~c%<`C$4XgI?2PeSo;Z922*!-^9cj*V43a855T(vS8@Xd`ui}D%PiX zQY|vIqzv7<2v5-p?Q})%H>iWALZ9@|>SCuP1c6J_5>4ipdQ6;H%K2HH)~1*^wjhp) z17HN_?RjchKLjaD>49GVoJz%t}zw0L;A zqTvw^Fdf$u6PM{oCnnBpgqS#>wM9(abrCNxx{Zmm6##|n&_et5#l+2q_Mtgl*VJ9K zG={t|3e73LPr3Vcb?S83_Ra|6dsQ8lW*Emeh;eLh&!N<{2%O>C;M_;au_+eGagq{5 z?_5W)-xL!EUO6TXyt>Hjv@vnu)oU6P2VNaf`{30~OdNRia*uNGYEw*{fQv%~JoS%4 zR2<3yYmK>jg>Z#cnPABP(@*LV$hn8q-uMgD2+6~ry;zQa-!sLwVu-Ea4m6nqN9BR{MtOq%e zGi$9E4`_7qX7$+!Yh$IP2(_)f4(Vd*T~B_yS!s;TJ3>IFTI2VG)|er+TH`xwG*d>00)xi6t=W@uswx_SXaU|RS}%CV zP{{F>GW_9To!lv+RWE{an7F@aBbC?9)C-=`%L~4nxTq%!uu)uML=hVA1611Dn>A$X z>7E38zQh>JIc7asj0qVKwM6n}jpIP<&59psc(Y2}mMV@depre--mKR}dG+RIZ`Q$8 zyjiOnZ&o@R-mIZQ;rmvdxbNAUwZDZo>vzbT>MIi+2o=h6mY%!bL>FAr^(I1M;GI7@ z-mFk^`U@h221O2IA!3Hi7LIGOE`^wYU)wACi>3CYK>368#Cqmqn-nORAmu^qW02`ZvW**^J_*->}4e+mw$S zLO-y+{;jp_YUP6u9%}yjLro7`=p4EiN$q$CKZYCk~i8pe>f(iy!$#EMW>E=EtS{qk`_g*h5z8t0hiiaB{(W zDZo;92==znte5xVP0$}bZ)Zz=Q6jvH7wCU^sQI?_#d|4%LigYO5MgxCeMe=`_lO&R zJqoM!zIqN2COGJ`z0jxVS3MvEvl$U)t#}NWA%jsuHr>nXUq3+|yzA+*|Ij$EEgZ7J zvS?mbx&G**Oa}je6Uf%Qq$m2LC(<;6fA{JM`{xAJ8vYGGzK;Pi4}v4T=jL-v14B-i zeJ(TtldAwgQ%Me-ERIr7?|n>1dQgx5Aop@Pp{*x{IU2lij#iuLp*pC_b#Z| zGYL1Qw-U%fDa?jXHP6vQ|Dj6rVNjrY^sm!!F@>6(nG?XeqZhyc!U%x5$BPuu^EEcD z)#zXLH1oXW<@8&<=0nZ58clb5BSCxBy}Oa2z-^>qUKy$)>nlIt=r40)pZ_ZSzKz%a zV{c{UD~s}g8)Y40XVq%`PkOF9Z`j%w%%Ia#6^-7BjIo&4WiPAcX3ra{2~)2VbhXbSyogNG&}-6 z>mi0?U0_oIC0Jko5UNBe-gpk^za6w0eMDh46_IRvik0tOgowsqmXRRvj^jMRW|4?% z)EgRG{HusP<$GA3873!TvQ8jLEAppUyq6E~iq0WiKGsv{24D+?ML(}CnzN-x_4;XV9+b8KJ7rm1(Rs0beudI(V+~zi(FVD zqp|IZO29fJAubme6+UILy1wLm%)G5$u;FO3m=;{rWx zQcwX9AN^GoYQMr|(I=7CATlr~c)wZyz3L&vv4`&}gan=-q+iCHTe{CkvVhLV?YIH8 zH({qszAVa>358c{4n6U!>Lc7>NgmreX&YqIboJ(@N{XXARsDj=TJf}q>&7H8EkejL z<}XGHwy>HpzcFL4(NMD|YRpd3XMsOLp8+EfFBEL$DrroLFohoTfNjvmP~VqU=MgJH zxm`}nxc3~V`_T$qE%iGCw(0=36tc0>TKFzOYXiO&;Lh*`4WWlhLKUkKgpcSqbUZQt zx$(hOW?zIo+C?gGT*f1BXTwx@$~5**4-}djGxM;&}{H@ zbOge13s@BJ;%aC?Q9oU-+|}!eP9PeT+G)6(;#0lm<-gC`-qC#bXSv|M09R|C{4rYl zB+MU*1(Eq{TfB>yv5Rgau}FW4c*EybDNu`-e}Sgfn%~t$lZM!JL?1b!Kk`|k!}>&X zG8b?N=BL`v-O3OE;>3{5gXxXVlq;X@(Gx!$mob=PPce%=Fa0bJmYd)6r4~4vBh&?u;zEYx?RjXMrijsf8T8wOV_hQDHs|=R5WlJT z*i|MW%>WS1lQ2rXIVITM(p>s`Tw)D18!Gy}Tp+Ab!3F(6)S|zgN%-Ze?0vYOfKA6n ze>-^h=(MjY`Pp*&3bU|IR-ZmNCFSP0Qgwd}sY`_PCZhTUITT`4{x& zItE0wFYbs|{$AOu@{-v!i})&#wPU;3U6Dp!i8_FE<5Ck>vw!=2^h=&F%@)V4G7BPmAX59c^|YT8K5PwsTBc&d40#5m@`Kh~S6>9|FGgopJ|=nt zV1jB+3C)x~iOy8e8Kc&ejF@gr$;f89A#%Aj-7o=e=|&lJV-+wlDW0yC|5D9RL7Ydg zj|xgVmWAZZSC}oRjQ9%sDsz}!+?k@YHXtjbpEAkZ`rQ{dUh!S%>s8)`OuoW*A;w$3 zd*PMeWd^o>_t{tOv+4C#usZX~-xW`og>%}lT>hCnUPG6pY;P_1u)>0WyotL40YH3R zcW>gZuG=Y4qjHK}`9L5fb1pV6obU>yv)UTd7!&Ic62%K$7l6x{&a&C351L&QLPl|X zO?UZgWlT6F{@BM8`X69DcfST>L%*P}@!fRyrU%u&%J$?t`YaLQE%D7edGs6-f%#Mj zHD&XLx0ZO%QBk4;`YGz@6*|q`JGk5HyF0n7efJjbb^(_Xyrjy3L%`&L8JN%-P!UJ& z0)tK^uw3|2^?}^c>Vu&3#e>*zxc`sbyY;Zjz#b#jL3gBCXG#al1DAc$Yn@se;aUwOtt{DsmS10ej*tX_I>on zHdF^9_5DHyZ-~(^_8^lV>8+gU_xd8_S<~nCZnkk2>&8`L4kMD zys|!73C%HDduw?IcU%IyC9&Np4H<=dHI|TNLh*;{fDf}i=**7g;^6t8x%jKU^7y~{ zr{D3|H(4i;KK(nd(Wh;yVvu7YKa1v{(5_$%SxL9dk49HBPVF;eDSa+|p0R34(J2j? zi-vsZ-@WpX<7_%DXCP}QaaQqS+KG)ZJ>g^hSEP>WF};8ieN5tvq{>;HaHzThCHI!QP#thV8x(ED5$oiSA?|~w!_I=p>3Hgq05C!a;rkkuZ)p zZrO@Cu%kbEBB&cbYpXGAabp$jt9DJ6BeG;~9PB~=#391;%65*UPsZ9VVC_sVQU4Bf zTy7N0EFN#xHc7Fq%JzOyW)FVdE|4;km}0E~Ho_W?Crjoinn7Xdtrd#Yn*u0^CmPEt z7seu;3%ry(jM-b~tGs>QjQeVD zU&wF&&ogMkVA0ui@g%UsethV~wUyigSB$e$Btg2INT?RX21!{6${9L_U8@I#>=i@@ z0gP?jF$X~OJ9}W)uau$fOd_l`wJhBAB%K2W?bz2x-w5QjqW{c8JbU=<1-?@JT6nbC z(63dZQN&EZv7|qdO^V6LNjt=)SQAR%WHlGsPL>CIy4W*IMJ5F3d;n*y_(1+5p(7Nm zK04Qh8y;b7UP$pb(@Oi&MOT<_b1h<$gDj-(9}3kBD<)_AQ0G}h>awJ79)xCw zm96mzzr1_@^Yc6lIKM`0YlT_MQ}$1j*d@;gZO5!!CU>T&0~zu}i!JO`EwWe5=^qcN zrV?7n)b%DwHOhb*QjP9JtoL%I#+`MQ`5-G-UKETK!jM@ZKg4-QKMb+Ulrxe(&XP_| z`o8K8CCWs=37G}}Dle~LBb=~pd*$;P)P*ngLH(yRQ&NEpm?_Aj))Q7Q&6JXg#!L+~ zQ=YQ{sgEOPC4$9I#GDOc&hQA(oFNB@dy?)pq=G@3!2#__4!(m&Sk66{e)C=RAkQUK zLfIOa%mH)SPUc|qWDXPqJjVx_Ogy&^%unV(iM!Hl(rN9|SFV(0KX_9X;DQ~_hTw_d zLobq#BO~rQd!hBA*QH=2KvQ>69lZ>Dq!orK z$=0lm)7V8eL$j>GNH(F0<=daxRxSnPPl`E!bY*jqILQp$Z?WjQNhw+auKZ7Pa6iBg z(;e{tSH+=iU*0GE&b9s7-In zr}pI!+e;COM|v34!JnhH=5u0-ns;j?js?#E=CQbmXLqm33ns$$_D;XIPgt1F3<607 z!43-7s;Hn*x#EWOQ^&ye0G#m>`waH)Qkg1oB=ft05J`*u{l%?g8|`!(Ik<~zI&F&) z1F80xbMX~=>#LmT8k)q(apPrO_F{+O!5*9BbO;efqe{puD|q|SrTD04G8D5M10!I= zzwwK>tHA0}FTb@Q<01C)LfHxzwMr13sf8Jg2AsiMfG`t%-LNjAJD?hSMC{^DJ^1<*jI7Q&j85l7ouP(`;( z%(rdnO24Nn;;9_NyyGX3yFA}B*qK;7a+F>vc zpd8oQp?q)oh!#A)k6Qlwiw_7dlscY!r;ZnVv=M7(>8oRw?f|Be_va@97r!_|=JQAC zSqkbrH8YK+5zns=7q{!f1D{Fy`^}tK7x4@hx#K+$~epZD&G;OnaS z1e)lErBTAN0!O*9XHsOH`a$y+M!+jKgF^*C>b1=b!df|PCZl?8W|QzS1AYr1g5`|x zF%WM1MX`}n7zc1*_};YT)b!9exf_`yI~Hi)?wz6JtZP;;Lw*=S3w#nSMDJ=P-1nv{zljM-QEY={@@-A0Aa461kKs~2T5mLmY94QC{xd3$Km}0zmT$uzUU=clf z3rK7d?9+8Mo!Z!F3#ecg)~a33zE1sNj{q6`p*NN{JhrGjh&rBN8KG*K_NzyQwhwWr zB7KdbQbjl>QPfl)&_-yvUUr1csEhFUplDatpRC1B%wBJ6+KI9K>eXu~^A1f$@~EO_ zEf+JfhGk%h6v^eL+hFGX=I4P16nA_>b1vQ8*!{}Y%u=wo%Hli@5N$5`BBkm5*-}DnYOi?Z-Wg z)xpI(c)7}Hn~y(OqXV3z=9- zmNKY^e`|#+-Q4@Pw3W)XQZ-*`S6iuSD>(ykw-A<)%0)$HCt1R?+BI<#%EGX^uB5AKn2c$37*mSnlHy?b<yAK~@I_;va9 zA301ri%0bA%Jz?Nzf1Qo4_&^O``@cndgqbDhY!CElp#kmGqmV2EX+?{AJJWpm*yXP z0y#Zp*^ig!AA9O}EL%atsAMc!hoWXvxAKyIxID`EbKfY!D@f7UvoU&G#;Y{Z>D| zb^dV%`)z)F+x+7U_T7HGd;W0-`|W;w`~2e!_Am3}FPndy!Tvfwe%<`z4EERi@$2Uw zPr-h}A0pVkRX zPrasq87|GU>PwgVJ)Jvep4XSd_h626Wu7O-Huos=yfCav=)!GqzEb9S?4Yo|My23+ z9r~Dg9{EGsO2P9wl`_xk<9Lu;3ZB=glzASeqg@Mu82#BAe;Ellizh80_;ss?rB+211K3wf@2RyI%0qr&n0 zI7>!na{;3p1nPW`_%{iWYJD54TB~>q(N_wgReG63*dzaY zrZ6|tCSBj&y!`{spML0*$X|yIL_$iW+#0XeH|hDH-XYJ2)msHoh{z2P4Twm;#$A{F zf`nks!e$oM6i)-(P&OBn#c4lH6X;t-XwfZdgb*_9)Az3JghBb#0e6b2Njt6VsM-RHTv&{rild1)d;3Dqz3VUsN0h`-1$oIoh524yqko^ zTKscu1nbdGV)$;XBU<@$HB1y3Ip#kF6ZRzQsxz^`g>YJ-*w zp3DV|m8>3GPVy_ykDq5>7xfbZzS1r@)qhD;&3@?Md2?KCnIRTU(fKykpy{tEP=!B+ckd2e`&A3cH_#y#qrJ1u~q&r z`Cpp$KK~Eu?c0`yy~VQX^#=>=tkc>*k=H)@1nj^C>IB(T!qc+r@ObVYOWU!?Q!c7> zf$QyDU#0~);;1YfKa1w@KLspD0P4XJz3H*a2P+46#VtRT{_tQu{YjB>%^%!5TST#( zY!2SBxtK?*5(RFkcfRLg%S-hQcOQJLzWGSK^CJg~Z$bml+FFx*(<5)URGk79=FOgxS$(;eerhgvgcf{Io>vU)Jqtr z=xBcWE}*2BufD$*{>A6tUuJCU{+?_eX<^9Dh%CX1HR_P^$AFXm#~^Z*`h^}yiS*;&fV;8V|Xu1UyE+` zXKpIX53eRy(?w`$iZC6oH9wcMqJ#1=gk4RjY?MTj|25Qdzt4ZwLp7?(f)H*3$Dr8? zVeg^1#jopb6??HG`!urB^zPaHu<8i$CGWsM`|p-&3Bg6{5#L%yFy$-Cwo}Mi#A$c% zvBBId!pQ5a=Woy_ASbA(9h+NmvIsl_Jh5$eq1nxjl3p(~x8d}_2N#gpmywZ)S4HWG zTObV!DYL>9hfwEOpz#aOvh=%K#tjuDnz|&COj=z+hW8;MmIv>Ad{P~J`;kvHS3VK? zTNby5i%PsM(f@^hZ++wUi;o zX*d@0(1X_m+|sK7-16rOI5sv{0i3KVTLW$_K6bsJy9x}p0idfvb^Fzz%2pf8!nFan zBf58e7>6S1^TW6s1)eQ2PN!9G4Y-?M3yizvYOvXPEsVp{X{EX0+JL+D)c_8I$>#^S z-B$%1d8DoaxG#&3eSsMFI%C|*mh1TSg3so4+=nhNLn-myAIfs27+29v=;@hE9U-73 zOlYbPPB_<<%G$Z}9t7ITh#@=L32#YyC9OhG+(h`j%SU<=#pTi*`)6|$cU=XhxOkkw zD1zb+5m?Y~l#kAkPi>v)T9Ay?GitbZR|cAGTfahx8+?PYUW(PuoEkJN-wRJLbAWkH?bVaOKHz4Z8$dCT$SWH3FDbUMLj%nllMJ zhuEG@hDb8X9}f~YUn|xQE-Cvkl8nr@E|jshj6yD{P{Mweb&en)dYsyc)V&9lt4R4t z$k#I%9cMR;+cD5w#PwrRT42Bg=xhB=YxY?p426xXlKjXugkJ>>$Tb%Q!`UY{ie`Uv z3H$Nr2iUQM3$H{YOOOKJrJvxLRR&kMYu?a2$vdtrO{ez4=*txM;$I^!q-S4jex4=? zLQ_|p^SXad-*|#=+)&UBOQD$1-^b{8uY8q?l9cFfT4;0VzEZR6TBpUvf38x$1ANMQhs6W4x3Cm!ZT^2CIfq{m01vF-(*b?4VRCEHGoEw z)ab0nmKvE!-bc9pD-#@Y2G%t&4mK?UFFTzUympx(L%?_esWAz~lM-(*=T6}>yB^h1 zWr~Qx!Jt$u2UtOv2XXJLi}p}g8f2S-V?AOo%ZmpQ=D}zfmA|VuE3i!zlsn?WcIc%= zX`KC=g`|p=o0>rGO0u|+0p{i5L+Z#Q4|B@MTZ@1`;^~5eyugeL#8zYQf9@xlHBr-C zaP-G>!Nnh?#<)43Yh>Js-N$cKyBYA--nhAV8mcTzSU}u%G6#hB9t0<&H|qtcUD0 z!Y;(Rt*DAVxAuXUm9_|I!KLpm!!I8jz8)j~MsAgA%o6pSuX17YAq*h0OBj%HI3jj= zLoBTlAJjAtoqY@xjK#+dyJ7FB9C!gDu{@jOKc#Kvh`Su#4)*B_ilJzp;Hh;@u{E~_ zE0hxMhZa^2aaNYe*mf7%AV)MLnyh~l?PtvOgFSS-pkGhzAJP$}2I_X7td1u@-6G8?7F#C`%ClWh&aJNNvYGDxM2&z+S`0fh*eAOKRV5 z=;qTs(n5sVPWp%gbs>xg{he{k*j+0>7^Oa|p7s-%t#mM$dx8j`qwyv}?9eW>V9p)V zXs01|TfZG4c2|454YB)`xlX6?E25O(TEaBqLlG64yDFU+A^LWMG}wF5^fea$F}pGoA)MLf;rt zdhQ)JS{FU{MKf;Yka!KqpbjhU)L1Ci#7*w35!us*()p(|_U6@LKmh?y+nI!t} zx8$xkw@VJ71%B?cFIv6ElVSw9q%$i&r!hChxECm{NZK?Yc)RxUo+6XAcGoJide7Zr+!LmtBsAYzDitz=NQ9q&5c^uOl zy{7e26}^?C{a7;xP&~wx<)!NBtD5x^zL7@66ygOIBl1Pe&fgu37AAcJKGLf-&`FWh zn)Fd#)A9zbBhEDddGOnDJX+_(9ze<-pn0Ay?~9YoOKBFL?fzz^J=6XDbo)aNZ3!YtS_w`%L7_Du{>hs^a5KGHujqR z$~nQ}3C$#zcd%@@kR7>sneHi`&6EPoA~zzL%G)f@OU1(D{#YcUTa)^Cs;radlJ2N` zVp^JT`lOk5cO|@}xbJpO1!PU70pXjTg7Gx<)P6@x%3=YlTd^k8A3h}2=U%dz{!CgE z|L_{vz$d!60L%ea;KB@rYBmZ_3gigyEn_Hz9XvV6fqzT@lce5pGa6377ttycFBZoZ z#7i^jkZjLn%xSEXG?N05@NG{Q-}Y?6w~d5vHEFv3&PIU%@zx^Y-xK`q;@|P^Z}9JE z_xIuUhtLOqRdW|sL-d`C&?gQFoDU(De_Wg!g4ik*Zj*Cmx!XyxP+(9!GGDorBbZ291)diyG}wU zFz)j}r|zVJ*00H=zVHV!sVxhirwNAH%%rl)zW}1}Yh}yTnaeZ{Y$yRcVzt<{FVeA# z&`#%xdDlvnQO1moJ<0SrvZ^E?ov`&SLKqLwE8Z7cXjW3DauZwS?c zj08>Bi!vS)G{7Afhn#3{n+xRbtQe531)L}UuGZbvgtgN=jcf`*xW#!=7^j5%2(z$2 zE5Zv^fFybx#j5PJ0V4o4Mw=jK{vLpx5gVp}rSUBQTX#%w(V*mwBtk4t z7BQ8Q&28JTR{+C8Nhm!Au|#wQqO5|?B6W1&geeIo zAV1XsGIQZ%m&~KujA$J3Ox)soilPeWN(ySBYw*mYeU=Zq!t57Ctp8~BrLkyM{s0%v z3!lE$qIr?0v1qo&h9lQ}`&j$-7qrmU`!NlQ3=t!e0?EYWhd~@gY@k$7W*|<>2<~FC zW)a0i2aKb-_v@0*;4_0!yjn{eK<*a~kBwIoL1Qh5pqVaJS`zx`USt?#_IUFSex(Xm*?-M{KPvgzt%VKXW8i^Mqb;`Nfkr!n9rS@9WeS$67e z%jiXQ%VJ_4_fiS9|~E>zdh+zvEa5Z+4PMMJgiX;z&QFO}rp! zB5LsJHg#*YH+BE|edW%_Duoms!DMjm=A)5nsEnPMkVJuhH5LK<~jFaQq?!tyGG~O{-y(UH@Mwj z{A4xj397PFY<&oRCOPZ4O4L5m5)9(dXLLc6ua}74TtcJQZ)kq>{j|pUN!3K@D%+{>He{qve{n;c-ARN-xx*gI$eFSVoMhN*v^4*)93OUB`E*5>E-_7 zbJhRH-MIkRRn+(Y+;i^TySsNcx#5`?aQ4b0kvulq%}hFdb@c5*lQ$ zEezh7!%~dr)-9lB`=xo{LaWXvU}jzh(L}7A(snYTOB3#6}8BAWk}1Ng@=ezZ8rvtd%n~6ZKo>s z0YpJKyq(r~I{_V^fIJ*Rq-|%d5vBRo5YgeTI3P56++_`!z8H3`e0^(V zdpsN+Wjp{!fks=|?d(zZ2g0}NrS0<&wLJ?A+Ucgm#3~fg(jXFza1c4o!L_!ZJ2wy# zD4W_LBe&l}T0^$qgKFVG_=q5GSF{6SxC48Ww_-mQ8|=>piUNbf>DxgQ<4@w&#_QX_N0de7>TyJBEkYDM|p}O@CT|0 z0)BHvP@8!n#R!m(Rfs`s2z{f94<57_x!Xu@b%?8^rn#%uSB}~-%#@7!l|B>Sj;4he zF+_HEw1UcJW-^T%fTSm;cj3_O z6?W)$+X}@P>l5)Cm3FteqKxNJAH6Skra8hMj&%6cv#liYhr{|rW5SvaooOczRpC)* zc)dfldTdEcQzKO=XSF-F$T;NF>#e1?dI=9}%>ca30qDR_C=R*f3ZfwoN0Jd7H172U zR%fSO@J6ElU{%nqVyr0-lq%WN@EZ$lC#n`STyY(ZsrjkYn#XVj_>`k+3e{K+A*`40 z;-FGiOJo=~;RU)o9lAU7NfL7;Ep$nX8;H8jTFXF$)7e0Tr@MjZ(N}gNHzGEg2Dpxp ze#|Yh!s+!6L#{Wab%TrJ8&w?K7p%x|m=EHkC9HG3lJ5y8_S2qV%w}Y8$3)8U9V316 zVc38|yy1bgDHGeuotn^r$l=QYfa?SQ%1QTCmNS z>BW;%w~Q!n1XFp5@>Ux<3xgXYW5)zyn$jSdGRt*vQHN1->txCeFh;<>hF7%ex~SIX z3FUP#+LX(PaIR5-imv9;Q9+>O|MLnaOiE0sIo&quzXLI&#?jd!J72*x;aBNKLXD%d zL+yFh=7h^Q(4=r&>9x#p*C3c$T1h5fnw@+JwPB5w?6SE1ghCzeO!pEBlot6*KyVV~ z(a?>8FcmRY279+b@%EtyNFs*qFpQ!RG1lWywox=9#+Vg6(@IT6jAjAp>2^XW*m)H# z3a@tZ!+-qfyPD4FXqQi1Y3MCdJDc-q@O=$td8b6PC9;K1ER+y|w8q-l5hkpcAX*)a zgA;$uH8JEACsa{F**J!S{h-m9}wuUNg-8lDOZxLeBBbA>d7 z1<6x!c0M!3FVzhv84_^iMUE>kQhzz9a|2xP(X^ttBY&N!IV7`fYcN~G9lo=}V7Aza zFc@RZ5+pfhWR6Q$LaVFPSZngk?aK^tuRKxQy($jcV$Cm- z?;u?{bnGnjx^lBdoY$4&c4rbF55<{id~ko>~R|T@HNk;Z$YMeY7>2 zDlt`=acCvGEJPcb(raYAFIVIztsyFOpKnA?Tc!+nQ~IF?NJF#9voiE6Gsm#xZM$*w zE>pdwj!9#T*0UN%yQ7({YVewR3Njy+6QB)oXmL~uZUGIpJDT;0yep0kG)**%YzQ-W zv;NM8@Wbd!clr$iHTs+U5YDq59j}~X^P?V{x2xgM@j(b9-O2oexzRc8#{wNRi&+OuhAg7SzQhjm0|?9ZI*y>lxwuPIQWe9=jU?H5u}1SN016O%8&}Bwhj^U zExtAiGTZOHzQ$XuMhHi?kafE%Mqu+wHV?17EqIfa;7I#s;rB*f&=)s_ZsR|w;H@+| z30MWh{f(|E;p~ZIjjWCx-8DJFZkkNknn4Lo4TKY@g9!>WqUXduzuV(=)NK3nQP1_h zBd+uHrDWYCp-RjECcV%Y%ubG!vKZnkU^2)maDWUu_lR1GCXy@pb(HJ`^))J66qyR@ zNM)c~W>|B>O@w26|A7_+waKTPpf*)>N?TC71T`l_p=nO$ZK;ul3CQw4lhv~8$|kEP zC7W-dA+DP!$n1YwW7>^61Wm!(JB9rwr?I!*obXml8FI5POoxyZdJhq@g@$REY7f!b z7usl|&1w2XK@D-vk03-;`Kd)R8N5iA&Tq)cn_Q~HN#fE|H+#~JK2v2|i%8`rlA?YH zvq$@3PF=5a(T(Rw1gG2h z0w59fx9|X2A_$4Q=;!)|` z0Ew(QZ@9f2Axj)RK130-)&=WYs~=}f&X4vNSnf%@1-B0sYMerqmi+44ta#4FN=Y6_ ztEK1%Ts7tn`*vCl6M*HUi$zWq(;Z|~5ihPT1aGd#v8W6a;wg+~C$TL}n{@dO(1);J zm*HM&O_|}Bv2)b0d#!fQnobt)OL=06Rxx7)Q_LhD=CHzrx+>q&CtAfkPy4t7}lfD~H&2 zPs;4NC0FEot!XOs7Hia6denRNo@uk|+jm|6mhvIYt~3qu{@-GDy-V`vw`O*oAXPZnQD!SkGG-!*0;s=;^b;?v;Df z7MEZr*la+((+{YdRQCUBv&)z_o06M2NEzJqp21z`_<_RPy_(uSJ!Nq0D9x>7aO}t{ z4RxGXFgUhGPQCkA!ub+J5F* z)PeQeo4q*E`ONVOKe^e9BOHri^YzSa{df}|vReN~XD=`#CeB{?F0k2)X^%I1VTQm1 zHU;WychKgbz|4`Q0CL*SJSe~PWK}R|hD&18t;)(rpcDtLf zKhh4_KJGZ?QZP+2MVHq>A&g_{P}j7XvyfS(<5lZ~H^<4PBZms2b}n=!Cpp&%d1f8- z)y&*i?BHczu|1R~W3-2pTsx`H!;1q=PUj2^E%|F07|sQ3vs5|9Fgx_f3z`}jgjG8O zBa!H=tY?-?o>2E>4QyM_z(?yG9+tCB{(hkt+xW0Oq}Bh2VP|2m+*@p4BqLQaH4Ci) zVUCJ|$y8KuVIMkl$9$3ub7nr>Lt2hSZJ5If;nUQ?mUEgx>0sbB%SOfBBAT^13Kx0x zG4rap8EBhhm^3wEPLc@(e&n<=^xD?OEc+zdtB8;$P-Cm8(NnB#VImJv8#4r+4r%!e zjjLIjqn^PEH|J!Zp@>FX4Kss7pgpL!lYrbdne8GK2}ykuO>chwsLVB-j64x)M@(r>+cweGx~D_+=ls z5bR#FooG~7-h>Fg#!g9+lE_r|?R;d5p4rLhR_@h1uooA>;!n%uNqjS^ewc%UczD2P zxji>sj*H_~=F;!;ZfsyU%C;Akt7E+aIW-e)18v5-YqZd5hW1jLv992dPek%H7IJBr zrcuJQB1`8}q{~82Df-%ZF^CXk9kzTFe%l=*s7c%IK+3EnYQ9i|ykKXwDCX3*HV=5Q zwx2(7qZWU70$xAAINnA+LbRlmLjre*kurfP)OS=s*INq;=hk|&fTM>GU zJJ|48h0#;#{OH1L7oviql3a^o#+%x#BC2PKXIftRBp?YqPejixyVH@W9zdD&CYR6PiQ@g7vP>eZoe!$ zA!w>H@1I=e;=?Y}45z8eykl~i^AEdBw2M&thWQq=Y}w!4{M5^2lFg1N?-WX`bQ_pV z%lYNeA;hU2$25KR*heSfn6DHiE^?g$$BHA@vc`$nhBZ&!ihC!Qe)yPy%}-tCZBM;S zMA{^Lcx*p*`y?zJKAf<1!!}6lLh`ZK44WH z1@8P3KfkPrG-Km-YO@D1U6r05 zfh>k|x4fwV5qy#~Eud44yB?R%goIkmPEY%4)ul6~`esej$ zubi#P&s3&XVyet{x-!L7b2-1Job6NQ6sshsn4vhEV|r}KnEa)_nVx)97vBYU>i#SG z+aNC-N(I+`GsGG!t6>+81Up~EReT*4Qk*4^pq&v*LWyi%uY?B{ac;ws7K zyv^s-h|c94SI#3QkjITKC;1RdknRZ#Kt`Gdn;*M;Zwq$gLiIyo<(|eC6*e|CI=ktT1a57&J5e@tYL6Cba;zoqJWWJ015``OoNZwM?NP|DVx=w9cdQT4(#T z>D3uW9C?%(SRenK_#CF!@mSXlml``Nu`< z5k~5NeA;E6X$&a&rhlC7f<^w(KelPgI7~j`ALnRQY?!<)do*R@i=T2u-Q&)@O|JLP zr#oW2?~^WhjS+M@oNU7!*e*R@>5!0}AeX#ibvI}|5jx?rfGqb~Nz#OPA$ApF*_n#Z zo!gBE+J#iAf2lMR$W;-8RzSM}5>$@W3;W&#ZuWjY1fsrXeG&VA*tn8(X4jAh?%Jol zo$mSq?B`m*-N-f}F_wx+!5ZTV5m*a1cp=>pMQf8IsLi&SPg+>hmK=LtQi$0r?JeWu z#a;NNLN;<0SleqUAhwTGahgK@@BIbV-EUxGLK+K*E6T7k&~2(1N?|;%~YV_{KRS9H!UNqJ7Y` z8DUaj8jhDEJ}bxA3hk`)Qy`HLEKOP*k-%|MJejsiptDq3Tdk3o^Wx|;dBhmtdf%@- z3=#Y@36qpwqBAV#D(J^pTG8z|Kt65u#`oIhPG9Lr-}FB#5az{u;=6#E76=ZG0y!5S z7Mm`z7LoOP1dvlH;D;%>fs>tf5$E3r2JAJ%>178Zs zR|AgdP}~=#2{HlRXqv&!N$M8|({ukOJmlU_dFwJ4NDF;>#I-oRWSRkD(+;v>AV<}| zDX0y$i6Pe_yNu$vRa1>RP2N9Y{-lv#7ReLmZ_CP;UGl{FTeJL9DV{igdzN21#}nt* zR{yTOaFYsTHv_Vhsx!r>MHnXU4Py+GJhBjskVSGQGT-kLo2;UzMU8Bjup40xo!BDH zL`2E!VuMmN?236xLFyZ&*sMfRCABivPz0^GsxQuFPo7M1&Eh;(Tyw6tnZ7u-jE5OL z3FvH35i3rdKx!(d7L0<2Huk%8z!gV_5jIxxa7TUP9UV?kv(VuL6flh6$;dTG^bJpE4|70|EG>42 zA1@4d1v-z26K-seb$fAyy=ZH^m|-uD*sW7k_v;f_pV3vn+e?&DNCer3rW%LjtFpZo z4btqP)|?i%E6Q7t#4w^c0@U`_QZJSO6nCabDwOH!!75)F8U5F4us26)~f8idJ48hfi+d$8KJJL#tO ziT`76Ao(OZ5GMV#G6K(dH*8*mGOkFffjz_~mtv)cG)?82?Gu46zl2Dd`1ft{C4M0c zybMe&n%+>~OI)ddS|ms*LJ7#!bO$8hL%K>RGHuu5?vRxdT(Wj-F6B@eNR>AZNlH!$ z!)(c*1Zr}hi~yZjpk?~*$<8fP+Cvl4?8(w_1yLfB!tK^$c8Mv{6MUjr0s(UBUb@K5 zS~zQG9`AS|e;(Td-bbo!B(tN?ge0Ayl%Z=wP{OMOaB31X$pR@S$G#w;VR6|j-E+`0 z7+yL}SN$$w=rym*4j_QH07@|jg32lN;sm zu+CxrtU_SU(Z@De4g-}jwz|&^S?0T{J?$J*VmPT$tTUfsx;>#@nQnDZUuIpMDs1gZ zu>x~-;=s0oUn@?=G9Xkikb_2PWGhC82tW%c*_Jx8XxRnMIY647kKvHG%;b|0kjW=F zrxz?XU+~nW+&4r;m2tV>4P@uiVJr8Eq7viTwWuMf&69w*fRWcyafxzr5VJ>vBg+I) z8~LV45PCV=iQh9B$Lnd^C!-eFy@d`L1{(8|q#&>>Z;9MC$p~g<44@RIhHOMc78C^} zvbS3YF_l^e`i%lxl2#6lk}E-0$!r0ewC=PdPCVfCcY_zW_hIs2Fqo#X1dz0cIM~F# z(QTH-S&yV$_oX09nv6RcH5EDw{s@X5ZD%$x^w`QVDD)b^QI`;UHxH zu^zH?WWUls_0`3VbRM#Fyc$Zs;fMEgo?u#lo(A1$Wa)RUgjZO-d``2%y!W%(k(ch=@#wlt1X{g&lGhbur?0YwO;ie zgJw>0qzeqyyO$N$qB)`|K_1fF)lJRYaWbuI9j}$MZ&3NxJs4aZmpgF9p?|Rc@`CoT zlCS|Bx>~MTCEu(PtBBKPIjL*@EZg4;Pes$jmXX_9rtnvbVDeVW{(nfV7Q)ODd(t@@ z8|1goqtaPq1^QqDG8>q)_@9Ql`J*3v@ViYQsvs?c6zixy5X)W?sR^&TETxtdF&PTLBDblZ%ssx!YTGeHUb>Cw*qqo9)56yZ-ipd%p1dU;n4qT~RrP9#%2>qdV}VN!{bD zqcoYyhaA0fpG>f9V(1djp#!Pt1~>?^GfTu>VSmtBpLU)vfI)e6g-BU8lS`Uj!4Ot% zG1q;5dvUx;6JjI`EVibN*qOFaarRF@zXr*TUlUlc4odmL_Vn51Z4p7R+;$ zDFq?8BM8^RPHgt>s94)thcc~C7uA;e}j+k$mJy!RW|fA~9} zy5n4U*SdQ?_1+Kv=&gTo+x1seDnWAf)t?lM>+$RqqU-$0{y6z-xG*i1y|wbx}#XlW{M`;pjk&12v&aD6BLybVDf( zjtlXI?qCGNLChCnn!f7GRlrfYu7Xtkn`SR^?l}iS&OJxatMx>#lWaKSR+#x*jwH1My7oL zl+D?5Hb9YI+ml-ux=?OT0{a#ssAl!NMz3MQz~kABQ+YVnt^Te(6z10UnSF=jH*QLwoJ zABtmBd@Zex_PXQZqUs}n%2$yuO`46rkE*WZA}+i_^%pAQM}^sy?{$RJoghXp=5*I^ zEiDa_pKJ7_n1?f^T`)D~S;2WQ=IM8}T4)zn08iG9#pM3ix*@I^NsJ)e^3-W3$Wl%y z-7`4>q0I5I<@2P7awa{$=G|LsLH};1-YBdv+Mk?(NyMpYqshMUPg-btO~-98jamHx z`!O~pnju8?Mb;pp3tu0FLm=$eiwP#Tqhw>LR?tDP^rwb6Oc>OYj`B*kG$qIFPKvuA z40|1%Ge-&RQb~$xi38IlsW|E|P7q3?q}W7G%j!dgw6(ix#4^EHs{dZtHW6rEvr(Ez zz=#HWs(67=C)zD+V9VP%0LiVYEs*8WLd`0LIAK`DQA8qkSe5EJ2ohb8P5OZ9A?xkc{knw^O$D(L5B8@VnC`$m(TFIe z0S}FjcGn8Ki5QD)P-e(@0n?)a^yNuSxaY+|xDPc*MS%b!w3;1ZBiCUcK%?_&k>MS% zxltea4zR&59K&(Iv1c9PWMV;vVW(RJ(6+VLs~*|6B~z6 zov^G~XpqCq6x5H~llG3{Q@KxFBx)5O))f$4ztwD3i$`G|v(RDQbyX?3JP!8ka@>Sc zqfxp7Kz~tS2$ZP_2nzU*YX#A-`fYTVFn3U`V((V_#9G0btJU|n)ta_1SGUKJBUA>Q zAQ9CU^C;6yNi-G45dCSHFn;PZoJCq!Z753#lYwf48E6Toqd^b?Xeb4aJ`G+bnba4y z*}L_Z7%xN8VWr7aIUB&Ig)#Eg&SpG7zPU#1RL9GM3X@tkCFv8jBAU+smmAUrrnHg= zJ!LSoHNohlp~Q}3$_<%?vk^H7F;sW24LSm5Ky4Uu(HI|pwHW6WXV(gIBvSMi={Xwf zk-XNC;>ZvIYwl9QOa&~+qH7EuC7C&q(lwK3;z9OKC>A*d4spz51GWYUs{n*wA;MwW z>8v(DERPtH`v`SJ$6Yd!x?i7gUxe6o50Dmm79@|TLj7@MPg~;9&fOCxlZRab$lJ#d z=?Ef-qWUYynw_SMJdA^i=RD?%2-Z_Vfn9lQjbTvzB{G3;{tLclVOmyAT5gR16=q^o zlKgb%p@&pp;#OFrL{LVN?l3@TY&$4$&&;=SN2U$>%~(pq!hXI~rN zR?9+2gKu3yG7vgs5?QNj(>>=X(?kBgWL%AXVrwWy{YRpzuX5bvVj zrKysk9CJKZklf81H@xrE9fLNoj~q!VzfH78kP4hu4P1a^IG|#G{M483j1Fjc*rLl@SoG}HL|TQu)?G>qur=y=m0(8;}XpMS`BD}QjcCx!Nz#6HWjy{ zpr%9@I|5Dl0lU^)Rmj_R}Gi82}7zj@claT1;06;|~Aex)vJz4ifO4gGAy- zednuAhrtxuwlaw%rIm(L4g>iEOn@1KHFoUv!aRPY?s<`ht_w>CSg2QOw>SxRP;V|r zF(-L*@>WDF)oZj?nnK<>MevE_ZCVRFc>HR5^S}ujkdHq|?`M#jexs}p>OmTi9(2u8 z9@fJHK1J6o+ar2-$fxLs$L2p#_P5FoS=|OLla!dZTT;o%8&9c2s57+q=U9)UA>)}S9 zqHC7zeR{abr|6nxyG;+b_!M2UY*Zp}B4A`~u-k;h;}gimu=;R8 zHjb~5O2SOvjcr?RrW-SOolLL+@PH^me`u|2$h7Z}351_Ze3?8loZFvC`BM3qFWt(t z(p6=mI|YcDMnE0%>|`jAjcg{6R}B!;9t95S6a`Xy9$z&8dcU)t*M)| zk0E%!I#z%9OvXFMrJ06iln&L-)T?SOH1z@42{092SSXje(%c$8AkXw2C0E`R+K6Oo zR}j!E)JMO0_n(4lz55RzJt=bkh0gsKX6`@gn+q9*-Ury&Dkz1RxqL9{^%NlvbcOf_ zKG6`gZZ{W}tqarl9Yu8kI#1>dtTEH5UcjED3Np|;X^C1VAzG@@ zh3ZpJJrl?V?2tASETSSaY(SSx60oU9>HYe&vZ>H^h1oJzTWZfX0S&}J6|{p3vw1d? z3={+`i^f=|g|@H70-_t}!f9E&IdFpgInP=Bl|L>U_2Rs)QKbg1>8pist^PYChXj4t zvx4~`4k>sha!7iKvPBn;L+ayajza=%W6{_PIefoktRFle0?9VRUsW? z9fjmeTu*exFW^#O`c14wcMG_SjmVPQ`7Ax$(0SnfnV)3^ZhtJE&UChZ!Fz z)0*l(D_($ojQo3D6QQrYFhzc3RC2q^Nk{4~`N}PVHArsruS2^_)Dq~M;-AC^2FXMS z5qXpW{oJa`G&}2HHKEA3a}#>TEN7G;dwe@ask*gs$1bCqTZR@jWl1MXiWzv^%~fQQ zh#+~)qZ~!sf*Ii34*62+lAG@Lm<=kg55ve!uepE;W7j6Tb!4fm`6^M&uGr9)4X}+8 zk!2)k8l9E(I+jVnQu1a+kS2ifoSS!zEJXnYKb@;g#49F8h zf?kAf=Kn7XE&}g=6DlxEr-XGqL)tpaiC7c|n3)Th0Ry^CF5ZMHop8i!3G~ZpqzQ0!JQId;;@>l~cHPUuYI> zI0?PO4>Y9j^`>&X@?jSoBDpP>Y>`>yO?`*#V=RKF$r{1}?s6vDY#bbvr)=eHuefW! zUBRut_E%;pkdFpdYf_X8a;4hbbj7!?jmf~q?QX*X1JGKyz4DRJ*1qtfgT_?<${7sR z_t{l%{x+Pho*7rXBwZs3(-A~#fgA1Y0ISkElT}nn@HR288|L6f>!sO@f^FXuSjZ7n`=tC2PhzG&t>4!`5 zUz5u-@kL`9_ipzE8{Y7Y;##94<3S2H>S&^lwFnm29nn4ETCF82GG@7y(#d@d7ffXA zXrek?17RO>x^vNIw#!QLO9ut{e0vG1&_^A9^}5gY9}X>zY$yvd@>C9`bp^+AxtmO~ zATa?=hRVV(5PvK#%A4j67QWBNwx);UXkid{1*>(33A{2~&K>hZY`7S=tcvDwXX7weEij9n;uZ96A%V0ykEeEf%7+>P zWIn_6G(#g)P{|WQ1@&TLsGwX^bTU92Jorbn1Qw8B?>ibGdcYz^A_=U2B?~MCDrKJ1 z#o;r7{d5KXLGR#XLg3#y;;4=QY7J=Jf?ez#@av&<#Nr|=dFw!x&;V3GoUQIcu#B5g z0;iH#n1coqRGycGEgH9;QfVLmriQy5a`&2oG*M~4hEy|$9*$U}3YDUzqMtD?{D0~X zBM`-;FnXJrB9ElI$T+e?b)}uL(i)DY12D^O4~fY3)riZ<>GGH~C6AIj;us~&)ktho zy$8Pf-L>kkGyOI8*LnWh>94EpN}QwnXRLUwBr_BHL_#`|e0sl@XSxVxW>dqoI(#>> zJP0i|?-irb7>T@#KmnhM2*Jc<0E9H!zf5qNX=Pd&a$>kbH!N`z6|=uCJ;u5>h7bce z)Znz`sf7P4eFawnLQ>J}%fKvf~JM8EjCZicecrI9~QhsEBJqtn|zB^QU9 z**{n@mLk2E@a0=Xep(fP{ezx>NO0@v(fMpWz0R#L3CdgZtE^sHIaUL=;$t*0+G*3|waHo-Aq-KhThUM!7RxBm4evQ} zLA=dIwKi~yVO1`=B&@zW^t)h)v9d%|LmXczdlgtdgsHaZQi_Uqsjqgz(%vA!7-@px z6IVPBKiC3VlHP^cU1WE%7{H`;I{`qFiEw=yII$4R2Bi?z-O#B)zS|E$@u(e5qP`HY zWRk_W$)Y4Wdk$cW1pCFgLJ3PWO$Rd)Za3v|!MCwnM+%TnHKZ05LJsX8vfvVQ1mRjh zQc;f6BEW234x0gJv00d(1sk3YEQAqcBf6blZyZyq1oVx5oycpM);F;vs{_S)x(=(f zx~&|sO>c>a1Iy_NSd)@$T_MaI-QAd;gsp@eWy0`0Z*$_r^EQ&i8Rv2#+4vf=TQWo% z-!<^@--w&t@tg3Jlhi!9JebfxkGyW&1ya*Nj~&=&G1b*KaG(JlzCxI&-X2yyE{6&T zki*1VFCq%F>1vR;ndD&-EBHS9iYNe7CfCbQNJuZyZ>fJJ^=fE!JnIT3I9Twjh+Oco zI9)DtuSgU({3-GrH2f)4Cw0n*>fq|UJLl=FO=JWBIbYADM?2d~W^3h_#X_rBs~rKc zLkoh&T+ri1+0K#32s+UP8WQm+c%7$aZ=0HdYkSRkJwk!~3KgTa_|>Elqe!5xiOyjYP`@8bZv8!^E1B!32V}bg4R5RxTal;zP0ao_9wog) zW0e|8qaDJD0|9GpY6uDwAb@i=AkYwG*tTj~VD5HE)X)SoIm)TPF}JoD1%6`<$1>_@ z;a3njLXe0YDO7*}X9{7kEGIx*ZLhJo%n}}Eua!bfi;YkSaV&^!TjV-Rp@sG;g*Y!z zqfqy4Rw({aE0m*kbA>kfLYX2ebP)V)rHKO&@jElNvys$GZjQkt8RQAWP9N?FVyWg# zMkwxD-VN89xGdAwI!)b)Ii*sp|M)YZ3#r@A+EAOEjz8u`F02csm`DRig)x17=$&fP z46x@UbVAXdLJAxw)1(UM&@9Z2UTw1+%@jeMyf!;m<%qVdP=RdA-Tt7-^cI_QN|35a z8OEXEzhRC%N(rt|Lh=i2tw!gWj^aA7@i(RxQ2-9HnWM?fu^L@1J*Aa^R-{kdN;tpH z-R6K5Y%y(=#jHQSs8NKw16_B+E>%_%_OnK}5Za-=7_8VC8`_(IAYyM`UFdrE`hs>; zWkS7>wv4JW`;~b46eaBlA#oZrADK7u>VoZt1|PF8vYPipGo?Zh1hTrClj(3j6qHa) z8zkca1oi9O_7;0;*VBIOiAJ8z4ec%NFzq*8@x1m{g;kf5O}mntt~iGTcu$Gj^V>Bu z4&65=C%Dhrq-`|gc~1tJM7Lrk`OSW_C$$Z$Gp7pydKWy?y)1 z{Ctk4B4-l1bEz8%&uiCqiiS1xnQ{d_qi(Y>on-vVtFFFm7bg!%6Vaf|Ug#)$#w1HR zIK7tO<_rptR$$1`$}3oesa`-$lK?^=q&7P779L_OJ=bFfF+Bw z&_j|IRxrr3FKg6x&EgK4mli?yG8gX~B&EwNVBhJs;g&jqXEj1a@|}v5xgyno54cqR zAPQOcDL*qlU`=_kxFB3B@he9Hmy&dLuv3G5%!J6D_gB0mF+c8)zq>DpTjnHT0wdux zmGdqmz%w=54T*id3 zdzUdYWZq@Wn`U-TK)SIl;0nT&GiUxU(A#U>=S9W9$2$R5h0qZooK$1Gs z?DA9Jw}Kq0ZmvtY{MZ+ElEH~x6Fh^QuB%mn@ z@9IIR;w89ApJTTzfGLo?&Mhrwg9KMsLnNreqkziM2N#dH%EeCr#Zv;{4+{>rj>73v zhJqG47}6%s&z|W00;TM1niAeY*q#PWDY?UN4PiKasARZaZgkQKhDA8?aIFkQTB04J zDWeTjtN*%`Z5_;Li%aXS93LOQiLb&zyI}YBklU2r&zN5HpwN zj<)cMXe|S`$=Tdt+)S{ZaR*eQGPX2t5OGN}{MT`RG9#!Z=W5WR%vLT;s3EcV>68JK zCYY{RLK7vCYBH)->r7-WX--jk01uSQmxTOi)arq+5LfV9YQf36z1A*<@rO6ua&It# zANGn76d%}cb3eOfIyM}{Y`|n}`0;Z|`NPU%;c@}CM%8dZgW%@x;}X)8CN;y;*?iol zx>UPFYpa4jBi9;2Y0gdD7aCNxm7f?*97 zTZ5^a`eenm(I$##6%7QL6ysK}BZGe?z097CTiByHZlmUwbC{$SBG5Y>E+8bfR-`>} zcVV%d7J$mGN+pCX?&#f3CLbUf+&t20<&Zf_LOfV^;J|Bt{qApmd&W!L(J;{Rshx|^ipv^Me^?{pCI#iBnmgA-y z{^Hu>$YPt#PEnHd3sp7#@2~yfH;++9l%*;1HOFe|<*#`UdPzEy_?(JUn3jI?3Z;GsZ7-crQ?6BX#-bRp`g z2F(P_8EFfmH+?BU(}bW(;sIa*;A^(QxPyi)05zbUAUs@ysLm4$(`Qx-Yk93^mvv$$ z)fiad3!XMBg)5Pr?5W?_4T9vzo5cN=%!-Nv1Fw{hp)ZSs2JZj-4%x0FpKWMBDU zVj;|_(>!HE;b$eNWov?Q;U<&NFi|7#pw`t(`qawsJ>%MNDD`?+eNSk12I%?o;|ce>r*_fw@l1GG$K2dN9 zVGvki62i0%G?RaJR{zA3^|~ML>;?z6!PE+tIOcAyVWWfSAt6|bV(joN<<8+*{MzFg z5d<2o!zQ(bJ?`U{soG*7lVTu4s4+XPQ-#hUoomj?5=4$~TLR7Hs(inYX{K)43Lz<> zBIAEjSi(W^2$(#%y*2gJh)2-V!SM*ZB{V0ouw}+J z3+FK;i|j8z#+Y3c;gKlMuu7P)9C(Frt%{P0442wv%hBW!yJCcWxHJ`k^H{9AB;E{@ zF;_BUG~>dT>=u9 z_A6?KbvK+Z!p&nyxw2$}M*{OuBr?;d7c{{+fgKmKqrJ+zX-L!VVblM2Z3mBzu^l2Y zjr(DLm4m-@f@P{N!IP}jnv`Ed71K5xxj^&GMa&T0ogBFGu`*j{yX$r$i;4qm&w(=s zeMb|KlF$}YJ*jCHLhIbvOf#uh99|0K#|`2p$-F7_m<8usM2WBgR1%w0LHx`jqC-Gu z23 z&JPZ9YH$b)p!Z-n=yQe8VMt45qc_mw4BDu&hM^(T*Ce>P8)I_FpDZblgvRud;MRf> zC(TKcXsq1Z?A+4(#UNy-Yu;Zz0~iW)@BWLC_qqeNE44Kn8R z8K^!ifWtG10??;BwF{vrO<@7HM7V_gu-#ldV*U5$?!UsliyATakeRuqjW20-s#`K9>${} zi{tF}lBXLQ$50s8Ig7Is@j&f%&Mnrr9>#I(RA{Wxh@4fzu{OT6RuFBecB&Qr_TgNU zC!^vgG)&oril6%Xs~EKao4ve#P9JioZ9@i-~&up%X5oNPm(ghTX zUe$K(mGmjzm6Uc@{;rUz^JN&DRW%VN#!uPB$-}J_Cy$XQZAyFhm}LO*5Fy`zt2Qnx z8Xj!;Xn07BA=VK>gz#V=6>p%pX#>@k_^5cb)p1+na7yt}@r@K|CDP5!LqSyhXB)s* z4hcL)p@RGwH3FVK5%6qKln0(R;ed%-({@x`JGfX>|I^n(S%Gn+d8E<79is^aRB!#)6miN``W+8Frrre4)C!04q_Hg&Q?{ zHm_L|*u3UkEaVICAe++-^P)K+#>}RU$ZJ7g3I`iDofoxE;aV2^CFif0ycHWYm@9M~ z=ol6IY8dh2m@Cd$DIx4xbQR2&z0W$+g%Kfer~k##v}ZNZvGy-4779`NkiVAT5)q;V zFFH5X589d^j#~@cEfncW$IZ!77~YCTo|H#`KEhSNVLy*B7p-~4pX!R+@JG6$9z?bx zfz8ge(leeys@t4mCe_V>Y>{>ik$VoCt9eBu6}iZLF*Lkkc%b`ZdQ+;AygztGm==uI z#w{J_L=1O94O;FGI$Bd~=n6j)0#N|DJ8BK~6LJ4@-SZyDwN)o{s18(nO^FcF!UK)r{12EA9hgc&ZX1%2 z2a|*dKNmQS$005<_0Pg80)I4%8jkMPqNb9C|6paQY*blnsX2yij&B1!t+CNJ+KjLQ zqVPkP71>yhr>8-I&^jeHB@g}^Bl8wX3H3S6Hxq%hocaY#2;*rN&~LsA$d+}1eDqeG zMw;q3^;MM=l1I`?ei+(9lBB}^2T4aq#4mK?EL@u?1irAFF$jsXLBn;w#MLeaTwyl` zCQh&yIRKQl7u=+Yw{F!p2i&d<8k>*{HTE;?sG1OSC{m`#-NbI);y}AuB@V? zcQf)y14qeMIUO@Ofvs2Nffek6c2VOINo$c% zvaYr}gBbA89jLg;T92!^) zZQhF>G$nRd<-3J|q5_VlZ0E*Bv9L~0nip?HYc?`?A|`PYP8Jn+R<(VA!{`S?%w z1<8WskAKGH&O-9FN4S0}s(yq8h0t!0eC$W9{fRrlLeOY%?tks*2D5;YoBo5`^OF6# z{8^a1X+PHwg#P17g~rDavnFr97_kl`DL4~POus2PMRpYuy;>{FxC$Jv4B?gZinBeq z`v1HPnl`Vw;Qt+xVOqMlCR?32t{I4H8nQATDfR$&Ea(b%6f(pe-L}D8U^m92XaKBM zJzy^>Gb;k>;Vl^U$P zNllutG;aa5YEUzl!35=~J*33#8ifgB1PoFgj(P~!Dd`ICwx`#N-)40=SCEN5lvo$_ zt63mbL-W9;&{YzwIDRdxtEjLpmVL@piZlyADzbvnO~X+kZ0d-k*5q2!27-#i2{nIW zr}S!vQe%wXPDrj3h-NB~Wmn}BA=9gN)CLO{iYU6L*pLb|)p@azsk9l8T2w5M;*M~G z0EuPL`6=Ynj3Csd!!O;m;KZ4(IAUrG3km^rh`BIui*JmfvF?E8aO1MYwSrR3>wPy+ za9>CnEE5zI9O1-!O3iB2bmYL)vuq$WO57A%zJzGf#Gh0Tb#PXNf&(?JMpv7G#Yl1p zfcD+$WX?>L(|#7zAjx&Vmg~|7nNd->E5wCke1`6{<-+4s0FFg!Ms8%D$-r{a!7y*z zn*06h4isK%v~S}J@(Msd_2&YEIV{v2o0SFz!$y!PG=VXoE|PSM{OW54i6n+rUqd}v zc(9e|!h-`Xx2876`$4s>qR?lgG80|n`RHJjwftim9qbqaI0h-#hn24v(}l@Qln|?iA_yt~c&dZ@`PP-jIAQfFT>l*wt;^S-;&mIj7b{k*3&}NCwa>UF6e`m~ZMR zyFj%9MpGpBu2%CE7Y5B#jOdXLYWtYEduYbHmetX?KNSX^Fib@-7QFVK-`&S>SMVC+ zdX8BwPRrHoT^ZD0^WZ+U4sIzDEvbR__0Rb*Vdf}npeb~!U3jHBEi8adO&GPIznx|AEf zGbyFRB)ViGc0HYGj*AaT+6V?5>eD3(7`Or2K6)tcR4m{T!2?{N1b!yG{o@)pc+tTF%ljr!F zTKRHPz@6@!FMoB7m%sn&8r#Q!S~cwA_e+Le4swbYzn?Vw>grd&8c3qcU(Lohl`~COcTazPw6}Y-d$eyyeQ0cSbhvwPWJ`T`bZ8^R z4cA9Tx_1nX4US&$qO;b&XyYa4+jad}?$JL4TZ3vaJqUu`T-&*h4EOZbM@C2LyTC2(v-uf`@80i}t>=^>| zo>A_Ad*{&bD3?Z|>s07)eRrR-Zmaig-8M?A28Z?x)O)wqchm<*hlcyM_6>Fq_~s7R z2fFvxhlc=QW8YxksNIYX?+v;N)u09V&*a+7)nQIqJPwBjxAYC~2wp*YE9qOfs=W@2 zRtz=U6_71!@Q1#vH*;+xFK{nA)Ahv#0Pv#v$j+g`5#eZE_eft)`b@VD_iY{}iY;ezT_s)6qH^oDPara=nY5nls?0zZ#Rz(Lx_XD zFQz188};sCn81!cjX&#jV+mtBVHb6mV5vL$whr@Vu)b$w&rolj0(Xos+<;_ow2uV; za-e&2eV~3>y$7Pc6aX&RsR2DWv{5W;(CQ1@oO>EqSj`p89Nn}MOPcWd{^#(};a zeWNd`?;O~B0enC_VXUVI&Qu>B9vbc**wejtWT<~VOmOddjTJRLNrz8=eQ!Eoegn$b z15HrX*80Mo17jl)#}ZE*JG)1>i86xV*(FCC^SM7JNN##P-1({*Iqp0%w%L$4kwvF$ zInM$;*ZU+TYBZnev43gC3|(jR4Q?5VN!RGs2)e1I7g5 z@Qj|}`Y7GZ;9?jV{^GGgC>-uu?;Y4XVdS4kIg5uOou0+LE4o)N-MqPX>B{<&?%vg# zS1s>cvSnrWswJzt*YvDd)w5>hn$8u=S1(&U(6@QGo3X|K9_q2-!_ey++$tPxYN`h3 zQRYLiCUK{+WlMYZY=mYv)-OXE4Ai^2Ae^qQ^^QhFf__A9WJGp#sb5FBx?Uiew6(s0 z)=&BQh6d%Sh^cBgr#^b75S~)T#Cm|Js|$YXxisjP@HrGX>j!sF{rQC^T0PS~=q_^+_n;bvjlF`>Y<#;nU0o^3fgl+0=~EXzNgkas zb*>bPQbL|C2sR_khxc}Mt+VT-il0QQh9S8jV?$kCjvXDMsZ&YpGtTJQv$TP#Y;-yy z;z;&Mz>@zgC5{Oi0o6@0SfJBGjhClbdG;AMrkZqhy~y894Z;h@>ce~KT4@d9L;9V> z?__?b@Y~ThG6D;TxAfHq5bB$5Zgtw?hqxlxg0Vpv1=~!6Ekz>^IG~pM26RddGGS^z|To(o%UhlidQP=}fM7clU4~qAK1*+UY(| zT3_EF0<^C;-rPM>U%9+1-bA`Iru6+KsIM!R$ETbUFN`nqY1eQS4)S@HEL|N77jN_B zr+LN(2BelSik%esH0jdXq@9)crO(pL^vp<2HDS}5NAz_%d?}*P=5V`=yuz2%G_-mI zGwg0heRLZF+ObzO-=1L;43=hp>cb0{xbE+fL?2nyH`LX$XCdUc(6i`HNxrmU8`hl} zd-)Z6nYAcBXJ|B@H$T^h^CS_+fJ?IJPq<1mx|yr`erRxDFA~U?zfR9`L!WG^tRSLl65ly>%r@M{x@?K{Y{oW&xBsGtv;|5bwNax-1;_Dk#|Ko zRpcN;@lHVd9Y9;lPax)>)ztL)%t`5bR=Rbg@2`<~up0vp%-2Wav7ItZ2kL`cF-toy zJDY!Rm{uilu2JTtzKq9%k1RnheQH&TJSo|0_$}wRg5OGhPgNaS#kcw=QazF1)2R-P zPM@d_Jq%uw2mXw{`pLXpTX!IU`%rx+sf`mfql{&b>>a@y0J%H6=^}fCdmk*svrkgy zk$rTgS2w5pY?9i+w^ggbC6s?HKlRgOaWi*@V~MA7Rr*w-;dIhO_i3I~BxRxjxzi9zh#{YU7gw>_ zr#^(8U|HC?Xekz_vo|rfdh4!j*HOOOb|Jro{KiK5URBTBJMo@v^}!fRw`Xhs?H&=k z8Cm28TM(q*>U$a8NUHkQ`taj@J+=kUPpmqg-UeX}))(&>>ZMT|yg9Y*%n4f%yhxzXe@;!I?ij}KYujxAN^ym1m0#@u4OHJkG zmexvJ`?TrR8Alv>)X~QrJ9C!HFniA2)YNxuw!s& z=dR(A(XrioF5CMm0q^i|>S6{!D!fyTCOGXVr_$tkoyPTZ{qL!hLTV69a;S_rSDu8^ z0Cm5DdgKt2T9tp6x|2SOr^tD9CO%B`i{W~AZ|sZ7=>1X3QaR~2YL8^o#m_(OtQU2~ zm&#id@4%czZyb#`*JHU&v5yDii=1uW)iqKlVQf_I@T-h6v__C&dxyrj8;r+B>W!?Z zPIy_#3B&E~T@*imh~XzE!QQSI0dJ$xRbU>Nk$B7S&<@Lh?HczXBmrViePAHY)SS2* zFYY#A9Fos+QG5~CcxX#JG&W2j8&r$p7s*>n);$Z-W;*;LZ*Ud}n$7iX-Mjk)1EAzvv?#uKD8`F~ zgJ;w}wib>K4~=aesK+oQ@tVN}G*Zpp8B1+fqlSm~##@Glt#ETK;j`(Jo46x>aXsD! zKoWL%s?~hq9tFaLl(|FobSA=2PO1g*j_!W3z0qwTbO&-G9vPChJ~UX5@e0MhmLZu8 zixwP>Q#brgc=aLZS=yfYvmMVq{n%>oO6X4fOnf(eUdMe0zo&gX;o(Ugly^|K9F#-+ z#6R=0c`eTgKfxirt#5EAq&_-?dM4Ucm&kj&(F`7XZ^DD~71evY;-GhC)ttG9`W~_z z8pQS*K#W)!`4_ozZlRnxz+ri|jqT_jlx#Dfl+|;$ugCj)E*=`PF_w+sZQVQZpF@S} zeE#od=x$|v7-3!df<+JWz4Vb3Cu0bIIl0s4H~VKVk?8O@D2vyVOJ46e^ym1 zzq2ym=gY)D4LT*q^aSG?Twi;!q|Y-z*$mr(M=4Dbj6*<@|G+L zf=BsYc0l_5NZ;1MZXBVw2dv9i VxNz=DM3_dA%328FFJxp}1L1n(~{P;SnQ9hnA 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. You can modify this before starting the chain via [the configs/server.json configuration file](../configs/server.json). +By default, the API is served at . You can modify this before starting the binary with `--api-address` and `--api-port`. ## Environment Variables diff --git a/local-interchain/go.mod b/local-interchain/go.mod index 368825faf..918b758e8 100644 --- a/local-interchain/go.mod +++ b/local-interchain/go.mod @@ -20,7 +20,7 @@ require ( github.com/spf13/cobra v1.7.0 github.com/strangelove-ventures/interchaintest/v8 v8.0.0-00010101000000-000000000000 github.com/tyler-smith/go-bip39 v1.1.0 - go.uber.org/zap v1.25.0 + go.uber.org/zap v1.26.0 ) @@ -44,8 +44,14 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect github.com/BurntSushi/toml v1.3.2 // indirect + github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect + github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect + github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect github.com/DataDog/zstd v1.5.5 // indirect + github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect + github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/StirlingMarketingGroup/go-namecase v1.0.0 // indirect github.com/avast/retry-go/v4 v4.5.0 // indirect github.com/aws/aws-sdk-go v1.44.224 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -62,7 +68,7 @@ require ( github.com/cockroachdb/pebble v0.0.0-20230817233644-564b068800e0 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - github.com/cometbft/cometbft v0.38.0-rc3 // indirect + github.com/cometbft/cometbft v0.38.0 // indirect github.com/cometbft/cometbft-db v0.8.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-db v1.0.0 // indirect @@ -77,6 +83,10 @@ require ( github.com/cosmos/ledger-cosmos-go v0.13.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set v1.8.0 // indirect + github.com/decred/base58 v1.0.4 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect @@ -89,6 +99,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/emicklei/dot v1.5.0 // indirect + github.com/ethereum/go-ethereum v1.12.1 // indirect github.com/fatih/color v1.15.0 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect @@ -98,6 +109,7 @@ require ( github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-stack/stack v1.8.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect @@ -105,7 +117,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/orderedcode v0.0.1 // indirect @@ -118,6 +130,8 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/gtank/merlin v0.1.1 // indirect + github.com/gtank/ristretto255 v0.1.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.7.1 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect @@ -130,31 +144,46 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/holiman/uint256 v1.2.3 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-libp2p v0.31.0 // indirect github.com/linxGnu/grocksdb v1.8.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/misko9/go-substrate-rpc-client/v4 v4.0.0-20230913220906-b988ea7da0c2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect github.com/mtibben/percent v0.2.1 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.11.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230110094441-db37f07504ce // indirect github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -162,6 +191,7 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 // indirect + github.com/pierrec/xxHash v0.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.16.0 // indirect @@ -174,6 +204,7 @@ require ( github.com/rs/cors v1.8.3 // indirect github.com/rs/zerolog v1.30.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -184,6 +215,7 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.6.0 // indirect + github.com/tyler-smith/go-bip32 v1.0.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.1 // indirect @@ -194,7 +226,7 @@ require ( golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.15.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/term v0.12.0 // indirect @@ -206,13 +238,15 @@ require ( google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect - google.golang.org/grpc v1.57.0 // indirect + google.golang.org/grpc v1.58.1 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.0 // indirect + lukechampine.com/blake3 v1.2.1 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect diff --git a/local-interchain/go.sum b/local-interchain/go.sum index e64a9ef1d..e62939b42 100644 --- a/local-interchain/go.sum +++ b/local-interchain/go.sum @@ -228,9 +228,19 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= +github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= +github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 h1:oknQF/iIhf5lVjbwjsVDzDByupRhga8nhA3NAmwyHDA= +github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420/go.mod h1:KYkiMX5AbOlXXYfxkrYPrRPV6EbVUALTQh5ptUOJzu8= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= @@ -240,6 +250,10 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StirlingMarketingGroup/go-namecase v1.0.0 h1:2CzaNtCzc4iNHirR+5ru9OzGg8rQp860gqLBFqRI02Y= +github.com/StirlingMarketingGroup/go-namecase v1.0.0/go.mod h1:ZsoSKcafcAzuBx+sndbxHu/RjDcDTrEdT4UvhniHfio= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= @@ -267,8 +281,6 @@ github.com/aws/aws-sdk-go v1.44.224 h1:09CiaaF35nRmxrzWZ2uRq5v6Ghg/d2RiPjZnSgtt+ github.com/aws/aws-sdk-go v1.44.224/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -286,6 +298,8 @@ github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipus github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -314,6 +328,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -339,8 +355,8 @@ github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/cometbft/cometbft v0.38.0-rc3 h1:Ly3eVPWoFu0y68PmZwLljucPdEBtfigZtqm+OV1W6dE= -github.com/cometbft/cometbft v0.38.0-rc3/go.mod h1:5Jz0Z8YsHSf0ZaAqGvi/ifioSdVFPtEGrm8Y9T/993k= +github.com/cometbft/cometbft v0.38.0 h1:ogKnpiPX7gxCvqTEF4ly25/wAxUqf181t30P3vqdpdc= +github.com/cometbft/cometbft v0.38.0/go.mod h1:5Jz0Z8YsHSf0ZaAqGvi/ifioSdVFPtEGrm8Y9T/993k= 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/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= @@ -359,6 +375,7 @@ github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQ github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= github.com/cosmos/cosmos-sdk v0.50.0-rc.0.0.20230905141004-6a9777fff287 h1:8KqZgZEGzPjqAL66YDOzAVFiDrLQA4Sr8RsoesV3Ymk= github.com/cosmos/cosmos-sdk v0.50.0-rc.0.0.20230905141004-6a9777fff287/go.mod h1:olbHxcVB4zWwnF+oNPbKIoEIO5HgHndzKUqdpuu4s34= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= @@ -386,8 +403,19 @@ github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/base58 v1.0.4 h1:QJC6B0E0rXOPA8U/kw2rP+qiRJsUaE2Er+pYb3siUeA= +github.com/decred/base58 v1.0.4/go.mod h1:jJswKPEdvpFpvf7dsDvFZyLT22xZ9lWqEByX38oGd9E= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.1 h1:18HurQ6DfHeNvwIjvOmrgr44bPdtVaQAe/WWwHg9goM= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.1/go.mod h1:XmyzkaXBy7ZvHdrTAlXAjpog8qKSAWa3ze7yqzWmgmc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= @@ -432,6 +460,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ethereum/go-ethereum v1.12.1 h1:1kXDPxhLfyySuQYIfRxVBGYuaHdxNNxevA73vjIwsgk= +github.com/ethereum/go-ethereum v1.12.1/go.mod h1:zKetLweqBR8ZS+1O9iJWI8DvmmD2NzD19apjEWDCsnw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= @@ -476,6 +506,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -490,14 +522,16 @@ github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= -github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= @@ -548,8 +582,9 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= @@ -599,8 +634,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= -github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= @@ -648,6 +683,11 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -696,6 +736,8 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= +github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= @@ -714,6 +756,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/jhump/protoreflect v1.15.2 h1:7YppbATX94jEt9KLAc5hICx4h6Yt3SaavhQRsIUEHP0= github.com/jhump/protoreflect v1.15.2/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -747,6 +791,8 @@ github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -765,6 +811,8 @@ github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-libp2p v0.31.0 h1:LFShhP8F6xthWiBBq3euxbKjZsoRajVEyBS9snfHxYg= +github.com/libp2p/go-libp2p v0.31.0/go.mod h1:W/FEK1c/t04PbRH3fA9i5oucu5YcgrG0JVoBWT1B7Eg= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linxGnu/grocksdb v1.8.0 h1:H4L/LhP7GOMf1j17oQAElHgVlbEje2h14A8Tz9cM2BE= @@ -795,8 +843,15 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/misko9/go-substrate-rpc-client/v4 v4.0.0-20230913220906-b988ea7da0c2 h1:G/cVeTAbB9S/6FSWWqpFV0v49hiuHLbJPu9hTZ0UR2A= +github.com/misko9/go-substrate-rpc-client/v4 v4.0.0-20230913220906-b988ea7da0c2/go.mod h1:Q5BxOd9FxJqYp4vCiLGVdetecPcWTmUQIu0bRigYosU= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -821,8 +876,24 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.11.0 h1:XqGyJ8ufbCE0HmTDwx2kPdsrQ36AGPZNZX6s6xfJH10= +github.com/multiformats/go-multiaddr v0.11.0/go.mod h1:gWUm0QLR4thQ6+ZF6SXUw8YjtwQSPapICM+NmCkxHSM= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -892,6 +963,8 @@ github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 h1:W04oB3d0J01W5j github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo= +github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -964,6 +1037,8 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0 github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -1006,6 +1081,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1026,8 +1102,14 @@ github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2l github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= +github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= +github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= +github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE= +github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= @@ -1085,8 +1167,9 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1095,6 +1178,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= @@ -1232,8 +1316,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1347,6 +1431,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1663,8 +1748,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.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.58.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58= +google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= 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= @@ -1699,6 +1784,8 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1727,6 +1814,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= +lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= +lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= diff --git a/local-interchain/interchain/config.go b/local-interchain/interchain/config.go index 79a3d2bb4..92748c156 100644 --- a/local-interchain/interchain/config.go +++ b/local-interchain/interchain/config.go @@ -46,14 +46,12 @@ func LoadConfig(installDir, chainCfgFile string) (*types.Config, error) { // configs Folder configsDir := filepath.Join(installDir, "configs") relayerFilePath := filepath.Join(configsDir, "relayer.json") - serverFilePath := filepath.Join(configsDir, "server.json") config, err := loadConfig(config, cfgFilePath) if err != nil { return nil, err } config, _ = loadConfig(config, relayerFilePath) - config, _ = loadConfig(config, serverFilePath) log.Println("Using directory:", installDir) log.Println("Using chain config:", cfgFilePath) diff --git a/local-interchain/interchain/handlers/actions.go b/local-interchain/interchain/handlers/actions.go index 5aba9fd2d..48ddecb3f 100644 --- a/local-interchain/interchain/handlers/actions.go +++ b/local-interchain/interchain/handlers/actions.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "strings" "github.com/strangelove-ventures/localinterchain/interchain/util" @@ -18,6 +19,7 @@ type actions struct { ctx context.Context ic *interchaintest.Interchain vals map[string]*cosmos.ChainNode + cc map[string]*cosmos.CosmosChain relayer ibc.Relayer eRep ibc.RelayerExecReporter @@ -29,11 +31,12 @@ type ActionHandler struct { Cmd string `json:"cmd"` } -func NewActions(ctx context.Context, ic *interchaintest.Interchain, vals map[string]*cosmos.ChainNode, relayer ibc.Relayer, eRep ibc.RelayerExecReporter) *actions { +func NewActions(ctx context.Context, ic *interchaintest.Interchain, cosmosChains map[string]*cosmos.CosmosChain, vals map[string]*cosmos.ChainNode, relayer ibc.Relayer, eRep ibc.RelayerExecReporter) *actions { return &actions{ ctx: ctx, ic: ic, vals: vals, + cc: cosmosChains, relayer: relayer, eRep: eRep, } @@ -43,7 +46,7 @@ func (a *actions) PostActions(w http.ResponseWriter, r *http.Request) { var ah ActionHandler err := json.NewDecoder(r.Body).Decode(&ah) if err != nil { - util.WriteError(w, err) + util.WriteError(w, fmt.Errorf("failed to decode json: %s", err)) return } @@ -69,19 +72,61 @@ func (a *actions) PostActions(w http.ResponseWriter, r *http.Request) { var output []byte var stdout, stderr []byte + val := a.vals[chainId] + + // parse out special commands if there are any. + cmdMap := make(map[string]string) + if strings.Contains(ah.Cmd, "=") { + for _, c := range strings.Split(ah.Cmd, ";") { + s := strings.Split(c, "=") + cmdMap[s[0]] = s[1] + } + } + // Node / Docker Linux Actions switch action { case "q", "query": - stdout, stderr, err = (a.vals[chainId]).ExecQuery(a.ctx, cmd...) + stdout, stderr, err = val.ExecQuery(a.ctx, cmd...) case "b", "bin", "binary": - stdout, stderr, err = (a.vals[chainId]).ExecBin(a.ctx, cmd...) + stdout, stderr, err = val.ExecBin(a.ctx, cmd...) case "e", "exec", "execute": - stdout, stderr, err = (a.vals[chainId]).Exec(a.ctx, cmd, []string{}) + stdout, stderr, err = val.Exec(a.ctx, cmd, []string{}) + case "recover-key": + kn := cmdMap["keyname"] + if err := val.RecoverKey(a.ctx, kn, cmdMap["mnemonic"]); err != nil { + if !strings.Contains(err.Error(), "aborted") { + util.WriteError(w, fmt.Errorf("failed to recover key: %s", err)) + return + } + } + stdout = []byte(fmt.Sprintf(`{"recovered_key":"%s"}`, kn)) + case "overwrite-genesis-file": + if err := val.OverwriteGenesisFile(a.ctx, []byte(cmdMap["new_genesis"])); err != nil { + util.WriteError(w, fmt.Errorf("failed to override genesis file: %s", err)) + return + } + stdout = []byte(fmt.Sprintf(`{"overwrote_genesis_file":"%s"}`, val.ContainerID())) + case "add-full-nodes": + chain := a.cc[chainId] + + amt, err := strconv.Atoi(cmdMap["amount"]) + if err != nil { + util.WriteError(w, fmt.Errorf("failed to convert amount to int: %s", err)) + return + } + + if err := chain.AddFullNodes(a.ctx, nil, amt); err != nil { + util.WriteError(w, fmt.Errorf("failed to add full nodes: %w", err)) + return + } + + stdout = []byte(fmt.Sprintf(`{"added_full_node":"%s"}`, cmdMap["amount"])) + case "dump-contract-state": + dumpContractState(w, r, cmdMap, a, val) } // Relayer Actions if the above is not used. if len(stdout) == 0 && len(stderr) == 0 && err == nil { - if err := a.relayerCheck(w, r); err != nil { return } @@ -160,3 +205,32 @@ func KillAll(ctx context.Context, ic *interchaintest.Interchain, vals map[string ic.Close() <-ctx.Done() } + +func dumpContractState(w http.ResponseWriter, r *http.Request, cmdMap map[string]string, a *actions, val *cosmos.ChainNode) { + contract, ok1 := cmdMap["contract"] + height, ok2 := cmdMap["height"] + if !ok1 || !ok2 { + util.WriteError(w, fmt.Errorf("contract or height not found in commands")) + return + } + + heightInt, err := strconv.ParseInt(height, 10, 64) + if err != nil { + util.WriteError(w, err) + return + } + + state, err := val.DumpContractState(a.ctx, contract, heightInt) + if err != nil { + util.WriteError(w, err) + return + } + + jsonRes, err := json.Marshal(state.Models) + if err != nil { + util.WriteError(w, err) + return + } + + util.Write(w, jsonRes) +} diff --git a/local-interchain/interchain/handlers/info.go b/local-interchain/interchain/handlers/info.go index f8d03ec5e..842072eac 100644 --- a/local-interchain/interchain/handlers/info.go +++ b/local-interchain/interchain/handlers/info.go @@ -1,25 +1,58 @@ package handlers import ( + "context" "encoding/json" + "fmt" "net/http" + "net/url" "os" "path/filepath" + "strconv" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" types "github.com/strangelove-ventures/localinterchain/interchain/types" util "github.com/strangelove-ventures/localinterchain/interchain/util" ) type info struct { - // ctx context.Context Config *types.Config InstallDir string + + // used to get information about state of the container + ctx context.Context + ic *interchaintest.Interchain + vals map[string]*cosmos.ChainNode + relayer ibc.Relayer + eRep ibc.RelayerExecReporter + + cc map[string]*cosmos.CosmosChain + + chainId string } -func NewInfo(cfg *types.Config, installDir string) *info { +func NewInfo( + cfg *types.Config, + installDir string, + ctx context.Context, + ic *interchaintest.Interchain, + cosmosChains map[string]*cosmos.CosmosChain, + vals map[string]*cosmos.ChainNode, + relayer ibc.Relayer, + eRep ibc.RelayerExecReporter, +) *info { return &info{ Config: cfg, InstallDir: installDir, + + ctx: ctx, + ic: ic, + vals: vals, + cc: cosmosChains, + relayer: relayer, + eRep: eRep, } } @@ -30,6 +63,103 @@ type GetInfo struct { } func (i *info) GetInfo(w http.ResponseWriter, r *http.Request) { + form := r.URL.Query() + + res, ok := form["request"] + if !ok { + get_logs(w, r, i) + return + } + + chainId, ok := form["chain_id"] + if !ok { + util.WriteError(w, fmt.Errorf("chain_id not found in query params")) + return + } + i.chainId = chainId[0] + + val := i.vals[i.chainId] + + switch res[0] { + case "logs": + get_logs(w, r, i) + case "config": + config(w, r, val) + case "name": + util.Write(w, []byte(val.Name())) + case "container_id": + util.Write(w, []byte(val.ContainerID())) + case "hostname": + util.Write(w, []byte(val.HostName())) + case "home_dir": + util.Write(w, []byte(val.HomeDir())) + case "is_above_sdk_47", "is_above_sdk_v47": + util.Write(w, []byte(strconv.FormatBool(val.IsAboveSDK47(i.ctx)))) + case "has_command": + hasCommand(w, r, form, i, val) + case "read_file": + readFile(w, r, form, i, val) + case "height": + height, _ := val.Height(i.ctx) + util.Write(w, []byte(strconv.Itoa(int(height)))) + case "build_information": + getBuildInfo(w, r, i, val) + case "genesis_file_content": + v, _ := val.GenesisFileContent(i.ctx) + util.Write(w, v) + default: + util.WriteError(w, fmt.Errorf("invalid get param: %s. does not exist", res[0])) + } +} + +func config(w http.ResponseWriter, r *http.Request, val *cosmos.ChainNode) { + cfg := val.Chain.Config() + jsonRes, err := MarshalIBCChainConfig(cfg) + if err != nil { + util.WriteError(w, fmt.Errorf("failed to marshal config: %w", err)) + return + } + + util.Write(w, []byte(jsonRes)) +} + +func hasCommand(w http.ResponseWriter, r *http.Request, form url.Values, i *info, val *cosmos.ChainNode) { + cmd, ok := form["command"] + if !ok { + util.WriteError(w, fmt.Errorf("command not found in query params")) + return + } + + util.Write(w, []byte(strconv.FormatBool(val.HasCommand(i.ctx, cmd[0])))) +} + +func readFile(w http.ResponseWriter, r *http.Request, form url.Values, i *info, val *cosmos.ChainNode) { + relPath, ok := form["relative_path"] + if !ok { + util.WriteError(w, fmt.Errorf("relPath not found in query params")) + return + } + + bz, err := val.ReadFile(i.ctx, relPath[0]) + if err != nil { + util.WriteError(w, err) + return + } + + util.Write(w, bz) +} + +func getBuildInfo(w http.ResponseWriter, r *http.Request, i *info, val *cosmos.ChainNode) { + bi := val.GetBuildInformation(i.ctx) + jsonRes, err := json.MarshalIndent(bi, "", " ") + if err != nil { + util.WriteError(w, err) + return + } + util.Write(w, []byte(jsonRes)) +} + +func get_logs(w http.ResponseWriter, r *http.Request, i *info) { fp := filepath.Join(i.InstallDir, "configs", "logs.json") bz, err := os.ReadFile(fp) diff --git a/local-interchain/interchain/handlers/types.go b/local-interchain/interchain/handlers/types.go new file mode 100644 index 000000000..754404034 --- /dev/null +++ b/local-interchain/interchain/handlers/types.go @@ -0,0 +1,43 @@ +package handlers + +import ( + "encoding/json" + + "github.com/strangelove-ventures/interchaintest/v8/ibc" +) + +type IbcChainConfigAlias struct { + Type string `json:"type"` + Name string `json:"name"` + ChainID string `json:"chain_id"` + Bin string `json:"bin"` + Bech32Prefix string `json:"bech32_prefix"` + Denom string `json:"denom"` + CoinType string `json:"coin_type"` + GasPrices string `json:"gas_prices"` + GasAdjustment float64 `json:"gas_adjustment"` + TrustingPeriod string `json:"trusting_period"` +} + +func (c *IbcChainConfigAlias) Marshal() ([]byte, error) { + return json.Marshal(c) +} + +func MarshalIBCChainConfig(cfg ibc.ChainConfig) ([]byte, error) { + jsonRes, err := json.MarshalIndent(IbcChainConfigAlias{ + Type: cfg.Type, + Name: cfg.Name, + ChainID: cfg.ChainID, + Bin: cfg.Bin, + Bech32Prefix: cfg.Bech32Prefix, + Denom: cfg.Denom, + CoinType: cfg.CoinType, + GasPrices: cfg.GasPrices, + GasAdjustment: cfg.GasAdjustment, + TrustingPeriod: cfg.TrustingPeriod, + }, "", " ") + if err != nil { + return nil, err + } + return jsonRes, nil +} diff --git a/local-interchain/interchain/handlers/uploader.go b/local-interchain/interchain/handlers/uploader.go index f2eec0466..4e2cb0e02 100644 --- a/local-interchain/interchain/handlers/uploader.go +++ b/local-interchain/interchain/handlers/uploader.go @@ -4,8 +4,9 @@ import ( "context" "encoding/json" "fmt" - "log" "net/http" + "os" + "path/filepath" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/localinterchain/interchain/util" @@ -18,8 +19,10 @@ type upload struct { type Uploader struct { ChainId string `json:"chain_id"` - KeyName string `json:"key_name"` - FileName string `json:"file_name"` + FilePath string `json:"file_path"` + + // Upload-Type: cosmwasm only + KeyName string `json:"key_name,omitempty"` } func NewUploader(ctx context.Context, vals map[string]*cosmos.ChainNode) *upload { @@ -37,7 +40,11 @@ func (u *upload) PostUpload(w http.ResponseWriter, r *http.Request) { return } - log.Printf("Uploader: %+v", u) + srcPath := upload.FilePath + if _, err := os.Stat(srcPath); os.IsNotExist(err) { + util.WriteError(w, fmt.Errorf("file %s does not exist on the source machine", srcPath)) + return + } chainId := upload.ChainId if _, ok := u.vals[chainId]; !ok { @@ -45,12 +52,28 @@ func (u *upload) PostUpload(w http.ResponseWriter, r *http.Request) { return } - codeId, err := u.vals[chainId].StoreContract(u.ctx, upload.KeyName, upload.FileName) + headerType := r.Header.Get("Upload-Type") + switch headerType { + case "cosmwasm": + // Upload & Store the contract on chain. + codeId, err := u.vals[chainId].StoreContract(u.ctx, upload.KeyName, srcPath) + if err != nil { + util.WriteError(w, err) + return + } - if err != nil { - util.WriteError(w, err) + util.Write(w, []byte(fmt.Sprintf(`{"code_id":%s}`, codeId))) return - } + default: + // Upload the file to the docker volume (val[0]). + _, file := filepath.Split(srcPath) + if err := u.vals[chainId].CopyFile(u.ctx, srcPath, file); err != nil { + util.WriteError(w, fmt.Errorf(`{"error":"writing contract file to docker volume: %w"}`, err)) + return + } - util.Write(w, []byte(fmt.Sprintf(`{"code_id":%s}`, codeId))) + home := u.vals[chainId].HomeDir() + fileLoc := filepath.Join(home, file) + util.Write(w, []byte(fmt.Sprintf(`{"success":"file uploaded to %s","location":"%s"}`, chainId, fileLoc))) + } } diff --git a/local-interchain/interchain/router/router.go b/local-interchain/interchain/router/router.go index 6f67847b4..2f47e92e3 100644 --- a/local-interchain/interchain/router/router.go +++ b/local-interchain/interchain/router/router.go @@ -20,13 +20,22 @@ type Route struct { Methods []string `json:"methods"` } -func NewRouter(ctx context.Context, ic *interchaintest.Interchain, config *ictypes.Config, vals map[string]*cosmos.ChainNode, relayer ibc.Relayer, eRep ibc.RelayerExecReporter, installDir string) *mux.Router { +func NewRouter( + ctx context.Context, + ic *interchaintest.Interchain, + config *ictypes.Config, + cosmosChains map[string]*cosmos.CosmosChain, + vals map[string]*cosmos.ChainNode, + relayer ibc.Relayer, + eRep ibc.RelayerExecReporter, + installDir string, +) *mux.Router { r := mux.NewRouter() - infoH := handlers.NewInfo(config, installDir) + infoH := handlers.NewInfo(config, installDir, ctx, ic, cosmosChains, vals, relayer, eRep) r.HandleFunc("/info", infoH.GetInfo).Methods(http.MethodGet) - actionsH := handlers.NewActions(ctx, ic, vals, relayer, eRep) + actionsH := handlers.NewActions(ctx, ic, cosmosChains, vals, relayer, eRep) r.HandleFunc("/", actionsH.PostActions).Methods(http.MethodPost) uploaderH := handlers.NewUploader(ctx, vals) diff --git a/local-interchain/interchain/start.go b/local-interchain/interchain/start.go index dba47f910..a5a3c6a60 100644 --- a/local-interchain/interchain/start.go +++ b/local-interchain/interchain/start.go @@ -15,10 +15,16 @@ import ( "github.com/strangelove-ventures/interchaintest/v8/testreporter" "github.com/strangelove-ventures/interchaintest/v8/testutil" "github.com/strangelove-ventures/localinterchain/interchain/router" + "github.com/strangelove-ventures/localinterchain/interchain/types" "go.uber.org/zap" ) -func StartChain(installDir, chainCfgFile string) { +type AppConfig struct { + Address string + Port uint16 +} + +func StartChain(installDir, chainCfgFile string, ac *AppConfig) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -160,7 +166,19 @@ func StartChain(installDir, chainCfgFile string) { // Starts a non blocking REST server to take action on the chain. go func() { - r := router.NewRouter(ctx, ic, config, vals, relayer, eRep, installDir) + cosmosChains := map[string]*cosmos.CosmosChain{} + for _, chain := range chains { + if cosmosChain, ok := chain.(*cosmos.CosmosChain); ok { + cosmosChains[cosmosChain.Config().ChainID] = cosmosChain + } + } + + r := router.NewRouter(ctx, ic, config, cosmosChains, vals, relayer, eRep, installDir) + + config.Server = types.RestServer{ + Host: ac.Address, + Port: fmt.Sprintf("%d", ac.Port), + } server := fmt.Sprintf("%s:%s", config.Server.Host, config.Server.Port) if err := http.ListenAndServe(server, r); err != nil { diff --git a/local-interchain/rust/.gitignore b/local-interchain/rust/.gitignore new file mode 100644 index 000000000..1de565933 --- /dev/null +++ b/local-interchain/rust/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/local-interchain/rust/Cargo.lock b/local-interchain/rust/Cargo.lock new file mode 100644 index 000000000..d5f7e02e7 --- /dev/null +++ b/local-interchain/rust/Cargo.lock @@ -0,0 +1,1683 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bnum" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128a44527fc0d6abf05f9eda748b9027536e12dff93f5acc8449f51583309350" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cosmwasm-crypto" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6fb22494cf7d23d0c348740e06e5c742070b2991fd41db77bba0bcfbae1a723" +dependencies = [ + "digest 0.10.7", + "ed25519-zebra", + "k256", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e199424486ea97d6b211db6387fd72e26b4a439d40cc23140b2d8305728055b" +dependencies = [ + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d89d680fb60439b7c5947b15f9c84b961b88d1f8a3b20c4bd178a3f87db8bae" +dependencies = [ + "base64", + "bnum", + "cosmwasm-crypto", + "cosmwasm-derive", + "derivative", + "forward_ref", + "hex", + "schemars", + "serde", + "serde-json-wasm", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek", + "hashbrown", + "hex", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.9", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8", + "signature", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + +[[package]] +name = "localic-bin" +version = "0.0.1" +dependencies = [ + "cosmwasm-std", + "localic-std", + "reqwest", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "localic-std" +version = "0.0.1" +dependencies = [ + "cosmwasm-std", + "reqwest", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a74ee2d7c2581cd139b42447d7d9389b889bdaad3a73f1ebb16f2a3237bb19c" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "schemars" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.4", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.38", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/local-interchain/rust/Cargo.toml b/local-interchain/rust/Cargo.toml new file mode 100644 index 000000000..b5b20fc65 --- /dev/null +++ b/local-interchain/rust/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] +members = ["main", "localic-std"] + +[workspace.dependencies] +reqwest = { version = "0.11.20", features = ["blocking", "json"]} +tokio = { version = "1.32.0", features = ["full"] } +serde_json = {version = "1.0.107"} +cosmwasm-std = "1.4.0" +thiserror = { version = "1.0.31" } + +localic-std = { path = "./localic-std"} \ No newline at end of file diff --git a/local-interchain/rust/Makefile b/local-interchain/rust/Makefile new file mode 100644 index 000000000..57def1e5f --- /dev/null +++ b/local-interchain/rust/Makefile @@ -0,0 +1,6 @@ +build-bin: + @echo "Building rust interchain..." + @cargo build --release + +run-bin: build-bin + @./target/release/localic-bin \ No newline at end of file diff --git a/local-interchain/rust/README.md b/local-interchain/rust/README.md new file mode 100644 index 000000000..55cb1def0 --- /dev/null +++ b/local-interchain/rust/README.md @@ -0,0 +1,227 @@ +# Rust Github CI + +Leveraging Local-Interchain, rust contract developers can now test their contracts in a rust based e2e environment with a simple CI pipeline. + +## Example + +[Example Contract Repo](https://github.com/Reecepbcups/interchaintest-rust-example) + +## Setup + +**requirements** - setup a standard git repository (like the example) for a cosmwasm contract. A good base is [cw-template](https://github.com/CosmWasm/cw-template). + +```bash +# create base directory +mkdir interchaintest +cd interchaintest + +# generate the testing binary +cargo init --name e2e_testing + +# add required dependencies to Cargo.toml +cargo add cosmwasm-std +cargo add localic-std --git https://github.com/strangelove-ventures/interchaintest + +# create the 2 sub directories +mkdir chains configs + +# Add the relayer config default. +echo '{ + "relayer": { + "docker_image": { + "repository": "ghcr.io/cosmos/relayer", + "version": "latest" + }, + "startup_flags": ["--block-history", "100"] + } +}' > configs/relayer.json + +# add a customizable SDK v47 chain config to the chains directory +echo '{ + "chains": [ + { + "name": "juno", + "chain_id": "localjuno-1", + "denom": "ujuno", + "binary": "junod", + "bech32_prefix": "juno", + "docker_image": { + "repository": "ghcr.io/cosmoscontracts/juno-e2e", + "version": "v17.0.0" + }, + "gas_prices": "0ujuno", + "chain_type": "cosmos", + "coin_type": 118, + "trusting_period": "112h", + "gas_adjustment": 2.0, + "number_vals": 1, + "number_node": 0, + "debugging": true, + "block_time": "500ms", + "encoding-options": ["juno"], + "genesis": { + "modify": [ + { + "key": "app_state.gov.params.voting_period", + "value": "15s" + }, + { + "key": "app_state.gov.params.max_deposit_period", + "value": "15s" + }, + { + "key": "app_state.gov.params.min_deposit.0.denom", + "value": "ujuno" + }, + { + "key": "app_state.gov.params.min_deposit.0.amount", + "value": "1" + } + ], + "accounts": [ + { + "name": "acc0", + "address": "juno1hj5fveer5cjtn4wd6wstzugjfdxzl0xps73ftl", + "amount": "10000000000%DENOM%", + "mnemonic": "decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry" + }, + { + "name": "acc1", + "address": "juno1efd63aw40lxf3n4mhf7dzhjkr453axurv2zdzk", + "amount": "10000000000%DENOM%", + "mnemonic": "wealth flavor believe regret funny network recall kiss grape useless pepper cram hint member few certain unveil rather brick bargain curious require crowd raise" + } + ], + "startup_commands": [ + "%BIN% keys add example-key-after --keyring-backend test --home %HOME%" + ] + } + } + ] +}' > chains/juno.json +``` + +## CI Configuration + +Add the following configuration to your workflows directory. + +```bash +mkdir .github/workflows +``` + +```yaml +# contract-e2e.yml +name: contract-e2e + +on: + # run on every PR and merge to main. + pull_request: + push: + branches: [ master, main ] + +# Ensures that only a single workflow per PR will run at a time. +# Cancels in-progress jobs if new commit is pushed. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + GO_VERSION: 1.21 + +jobs: + # Builds local-interchain binary + build: + runs-on: ubuntu-latest + name: build + steps: + - name: Checkout interchaintest + uses: actions/checkout@v4 + with: + repository: strangelove-ventures/interchaintest + path: interchaintest + # ref: 'reece/rust' # branch, commit, tag + + - name: Setup go ${{ env.GO_VERSION }} + uses: actions/setup-go@v4 + with: + go-version: ${{ env.GO_VERSION }} + + - name: build local-interchain + run: cd interchaintest/local-interchain && go mod tidy && make install + + - name: Upload localic artifact + uses: actions/upload-artifact@v3 + with: + name: local-ic + path: ~/go/bin/local-ic + + contract-e2e: + needs: build + name: contract e2e + runs-on: ubuntu-latest + # defaults: + # run: + # working-directory: ./nested-path-here + strategy: + fail-fast: false + + steps: + - name: checkout this repo (contracts) + uses: actions/checkout@v3 + + - name: Install latest toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + target: wasm32-unknown-unknown + override: true + + - name: Download Tarball Artifact + uses: actions/download-artifact@v3 + with: + name: local-ic + path: /tmp + + - name: Make local-ic executable + run: chmod +x /tmp/local-ic + + # TODO: modify me to match your setup + - name: Compile contract + run: make compile + + # TODO: You can change `juno` here to any config in the chains/ directory + # The `&` at the background allows it to run in the background of the CI pipeline. + - name: Start background ibc local-interchain + run: ICTEST_HOME=./interchaintest /tmp/local-ic start juno --api-port 8080 & + + # TODO: run the rust binary e2e test. (scripts, makefile, or just work here.) + - name: Run Rust E2E Script + run: make run-test + + - name: Cleanup + run: killall local-ic && exit 0 +``` + +## Makefile + +The following is an example makefile for the above CI. You can also use bash scripts or just files in the root directory. + +```makefile +#!/usr/bin/make -f +VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') +COMMIT := $(shell git log -1 --format='%H') + +CURRENT_DIR := $(shell pwd) +BASE_DIR := $(shell basename $(CURRENT_DIR)) + +compile: + @echo "Compiling Contract $(COMMIT)..." + @docker run --rm -v "$(CURRENT_DIR)":/code \ + --mount type=volume,source="$(BASE_DIR)_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.12.13 + +run-test: + cd interchaintest && cargo run --package e2e_testing --bin e2e_testing +``` diff --git a/local-interchain/rust/localic-std/Cargo.toml b/local-interchain/rust/localic-std/Cargo.toml new file mode 100644 index 000000000..49fc124d0 --- /dev/null +++ b/local-interchain/rust/localic-std/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "localic-std" +version = "0.0.1" +edition = "2021" +authors = ["Strangelove Developers"] + +[lib] +name = "localic_std" +path = "./src/lib.rs" + +[dependencies] +reqwest.workspace = true +tokio.workspace = true +serde_json.workspace = true +cosmwasm-std.workspace = true +thiserror.workspace = true diff --git a/local-interchain/rust/localic-std/Makefile b/local-interchain/rust/localic-std/Makefile new file mode 100644 index 000000000..7fc60e6fd --- /dev/null +++ b/local-interchain/rust/localic-std/Makefile @@ -0,0 +1,6 @@ +.PHONY: lint +lint: + @echo "Running linter..." + @cargo fmt --all + @cargo clippy -- -D warnings + @cargo clippy -- -W clippy::pedantic -W clippy::correctness -W clippy::branches_sharing_code \ No newline at end of file diff --git a/local-interchain/rust/localic-std/src/errors.rs b/local-interchain/rust/localic-std/src/errors.rs new file mode 100644 index 000000000..59c03e020 --- /dev/null +++ b/local-interchain/rust/localic-std/src/errors.rs @@ -0,0 +1,43 @@ +use thiserror::Error; + +#[derive(Error, Debug, Clone)] +pub enum LocalError { + #[error("{{msg}}")] + Custom { msg: String }, + + #[error("This err returned is NotImplemented")] + NotImplemented {}, + + #[error("the transaction hash was not found.")] + TxHashNotFound {}, + + #[error("file upload failed for path: {path}. reason: {reason}")] + UploadFailed { path: String, reason: String }, + + #[error("transaction was not successful. status: {code_status}. log: {raw_log}")] + TxNotSuccessful { code_status: i64, raw_log: String }, + + #[error("contract address not found. {events}")] + ContractAddressNotFound { events: String }, + + #[error("key_bech32 failed. reason: {reason}")] + KeyBech32Failed { reason: String }, + + #[error("This CosmWasm object has no value for: {value_type}")] + CWValueIsNone { value_type: String }, + + #[error("API URL is not found.")] + ApiNotFound {}, + + #[error("Chain ID is not found.")] + ChainIdNotFound {}, + + #[error("The local-interchain API server did not start in time.")] + ServerDidNotStart {}, + + #[error("Could not find the SDK status code in the Tx data. {reason}. tx_res: {tx_res}")] + SdkTransactionStatusCodeNotFound { reason: String, tx_res: String }, + + #[error("Could not get filesystem files. {error}")] + GetFilesError { error: String }, +} diff --git a/local-interchain/rust/localic-std/src/filesystem.rs b/local-interchain/rust/localic-std/src/filesystem.rs new file mode 100644 index 000000000..92b7dfb5d --- /dev/null +++ b/local-interchain/rust/localic-std/src/filesystem.rs @@ -0,0 +1,25 @@ +use crate::{errors::LocalError, transactions::ChainRequestBuilder}; + +/// # Errors +/// +/// Returns `Err` if the files can not be found. +pub fn get_files(rb: &ChainRequestBuilder, absolute_path: &str) -> Result, LocalError> { + let cmd: String = format!("ls {absolute_path}"); + let res = rb.exec(cmd.as_str(), true); + + if let Some(err) = res["error"].as_str() { + return Err(LocalError::GetFilesError { + error: err.to_string(), + }); + }; + + let text = res["text"].as_str(); + + let Some(text) = text else { return Ok(vec![]) }; + + Ok(text + .split('\n') + .filter(|s| !s.is_empty()) + .map(std::string::ToString::to_string) + .collect()) +} diff --git a/local-interchain/rust/localic-std/src/lib.rs b/local-interchain/rust/localic-std/src/lib.rs new file mode 100644 index 000000000..722b42fd6 --- /dev/null +++ b/local-interchain/rust/localic-std/src/lib.rs @@ -0,0 +1,10 @@ +pub mod errors; +pub mod filesystem; +pub mod polling; +pub mod transactions; +pub mod types; + +pub mod node; +pub mod relayer; + +pub mod modules; diff --git a/local-interchain/rust/localic-std/src/modules/bank.rs b/local-interchain/rust/localic-std/src/modules/bank.rs new file mode 100644 index 000000000..3413445cf --- /dev/null +++ b/local-interchain/rust/localic-std/src/modules/bank.rs @@ -0,0 +1,41 @@ +use cosmwasm_std::Coin; +use serde_json::Value; + +use crate::{errors::LocalError, transactions::ChainRequestBuilder, types::get_coin_from_json}; + +/// # Errors +/// +/// Returns `Err` if the transaction fails (ex: not enough balance, fees, or gas). +pub fn send( + rb: &ChainRequestBuilder, + from_key: &str, + to_address: &str, + tokens: &[Coin], + fee: &Coin, +) -> Result { + let str_coins = tokens + .iter() + .map(|coin| format!("{}{}", coin.amount, coin.denom)) + .collect::>() + .join(","); + + let cmd = + format!("tx bank send {from_key} {to_address} {str_coins} --fees={fee} --output=json"); + rb.tx(&cmd, true) +} + +pub fn get_balance(req_builder: &ChainRequestBuilder, address: &str) -> Vec { + let res = req_builder.query(&format!("q bank balances {address}"), false); + let Some(balances) = res["balances"].as_array() else { return vec![] }; + + let coins: Vec = balances.iter().map(get_coin_from_json).collect(); + coins +} + +pub fn get_total_supply(req_builder: &ChainRequestBuilder) -> Vec { + let res = req_builder.query("q bank total", false); + let Some(supplies) = res["supply"].as_array() else { return vec![] }; + + let coins: Vec = supplies.iter().map(get_coin_from_json).collect(); + coins +} diff --git a/local-interchain/rust/localic-std/src/modules/cosmwasm.rs b/local-interchain/rust/localic-std/src/modules/cosmwasm.rs new file mode 100644 index 000000000..4044731a2 --- /dev/null +++ b/local-interchain/rust/localic-std/src/modules/cosmwasm.rs @@ -0,0 +1,319 @@ +use std::path::{Path, PathBuf}; + +use serde_json::Value; + +use crate::{ + errors::LocalError, + relayer::Relayer, + transactions::ChainRequestBuilder, + types::{Contract, TransactionResponse}, +}; + +#[derive(Clone)] +pub struct CosmWasm<'a> { + rb: &'a ChainRequestBuilder, + + pub file_path: Option, + pub code_id: Option, + pub contract_addr: Option, +} + +impl CosmWasm<'_> { + #[must_use] + pub fn new(rb: &ChainRequestBuilder) -> CosmWasm { + CosmWasm { + rb, + file_path: None, + code_id: None, + contract_addr: None, + } + } + + #[must_use] + pub fn new_from_existing( + rb: &ChainRequestBuilder, + file_path: Option, + code_id: Option, + contract_addr: Option, + ) -> CosmWasm { + CosmWasm { + rb, + file_path, + code_id, + contract_addr, + } + } + + /// # Errors + /// + /// Returns `Err` if the `code_id` is not found when uploading the contract. + pub fn store(&mut self, key_name: &str, abs_path: &Path) -> Result { + // TODO: add cache + println!( + "uploading contract to {}: {}", + self.rb.chain_id.as_str(), + abs_path.to_str().unwrap_or_default() + ); + match self.rb.upload_contract(key_name, abs_path) { + Ok(code_id) => { + self.code_id = Some(code_id); + self.file_path = Some(abs_path.to_path_buf()); + Ok(code_id) + } + Err(e) => Err(e), + } + } + + /// # Errors + /// + /// Returns `Err` if the contract address is not found in the transaction logs. + pub fn instantiate( + &mut self, + account_key: &str, + msg: &str, + label: &str, + admin: Option<&str>, + flags: &str, + ) -> Result { + let code_id: u64 = match &self.code_id { + Some(code) => code.to_owned(), + None => { + return Err(LocalError::CWValueIsNone { + value_type: "code_id".to_string(), + }) + } + }; + + match contract_instantiate(self.rb, account_key, code_id, msg, label, admin, flags) { + Ok(contract) => { + self.contract_addr = Some(contract.address.clone()); + Ok(contract) + } + Err(e) => Err(e), + } + } + + /// # Errors + /// + /// Returns `Err` if the sdk status code can not be found in the JSON blob. + pub fn execute( + &self, + account_key: &str, + msg: &str, + flags: &str, + ) -> Result { + let contract_addr: &str = match &self.contract_addr { + Some(addr) => addr.as_ref(), + None => { + return Err(LocalError::CWValueIsNone { + value_type: "contract_addr".to_string(), + }) + } + }; + contract_execute(self.rb, contract_addr, account_key, msg, flags) + } + + /// # Panics + /// + /// Panics if the contract address already set in the `CosmWasm` object. + #[must_use] + pub fn query(&self, msg: &str) -> Value { + let contract_addr: &str = match &self.contract_addr { + Some(addr) => addr.as_ref(), + None => panic!("contract_addr is none"), + }; + contract_query(self.rb, contract_addr, msg) + } + + // make a query request which uses a serde_json::Value as the msg + #[must_use] + pub fn query_value(&self, msg: &Value) -> Value { + self.query(msg.to_string().as_str()) + } + + /// # Errors + /// + /// Returns `Err` if the contract address is not found in the transaction logs. + pub fn create_wasm_connection( + &self, + r: &Relayer, + path: &str, + dst: &CosmWasm, + order: &str, + version: &str, + ) -> Result { + let contract_addr: String = match &self.contract_addr { + Some(addr) => addr.to_string(), + None => { + return Err(LocalError::CWValueIsNone { + value_type: "contract_addr".to_string(), + }) + } + }; + + let dst_contract_addr: String = match &dst.contract_addr { + Some(addr) => addr.to_string(), + None => { + return Err(LocalError::CWValueIsNone { + value_type: "dst.contract_addr".to_string(), + }) + } + }; + + let src: String = format!("wasm.{}", contract_addr.as_str()); + let dst = format!("wasm.{}", dst_contract_addr.as_str()); + + r.create_channel(path, src.as_str(), dst.as_str(), order, version) + } +} + +/// # Errors +/// +/// Returns `Err` if the transaction fails or the contract address is not found. +pub fn contract_instantiate( + rb: &ChainRequestBuilder, + account_key: &str, + code_id: u64, + msg: &str, + label: &str, + admin: Option<&str>, + flags: &str, +) -> Result { + let mut updated_flags = flags.to_string(); + if let Some(admin) = admin { + updated_flags = format!("{flags} --admin={admin}"); + } else if !flags.contains("--no-admin") { + updated_flags = format!("{flags} --no-admin"); + } + + updated_flags = updated_flags.trim().to_string(); + + let mut cmd = format!("tx wasm instantiate {code_id} {msg} --label={label} --from={account_key} --output=json --gas=auto --gas-adjustment=3.0"); + if !updated_flags.is_empty() { + cmd = format!("{cmd} {updated_flags}"); + } + + let res = rb.tx(cmd.as_str(), false)?; + + let tx_hash: Option = rb.get_tx_hash(&res); + let raw_log: Option = rb.get_raw_log(&res); + + if raw_log.is_some() { + println!("raw_log: {}", raw_log.unwrap_or_default()); + } + + let contract_addr = get_contract_address(rb, tx_hash.clone().unwrap_or_default().as_str()); + match contract_addr { + Ok(contract_addr) => Ok(Contract { + address: contract_addr, + tx_hash: tx_hash.unwrap_or_default(), + admin: admin.map(std::string::ToString::to_string), + }), + Err(e) => Err(e), + } +} + +/// # Errors +/// +/// Returns `Err` if the SDK response code can not be found in the transaction data. +pub fn contract_execute( + rb: &ChainRequestBuilder, + contract_addr: &str, + account_key: &str, + msg: &str, + flags: &str, +) -> Result { + let mut cmd = format!("tx wasm execute {contract_addr} {msg} --from={account_key} {flags}"); + cmd = cmd.trim().to_string(); + + let updated_flags = flags.to_string(); + if !updated_flags.is_empty() { + cmd = format!("{cmd} {updated_flags}"); + } + + let res = match rb.tx(cmd.as_str(), false) { + Ok(res) => res, + Err(e) => { + return Err(e); + } + }; + + let tx_hash = rb.get_tx_hash(&res); + let raw_log = rb.get_raw_log(&res); + + let status_code = match rb.get_sdk_status_code(&res) { + Ok(code_id) => code_id, + Err(e) => { + return Err(e); + } + }; + + if let Some(tx_raw_log) = &raw_log { + println!("raw_log: {tx_raw_log}"); + } + + Ok(TransactionResponse { + status_code, + tx_hash, + raw_log, + }) +} + +#[must_use] +pub fn contract_query(rb: &ChainRequestBuilder, contract_addr: &str, msg: &str) -> Value { + let cmd = format!("query wasm contract-state smart {contract_addr} {msg}",); + rb.query(&cmd, false) +} + +/// # Errors +/// +/// Returns `Err` if the contract address is not found in the transaction logs. +pub fn get_contract_address(rb: &ChainRequestBuilder, tx_hash: &str) -> Result { + let mut last_error = LocalError::ContractAddressNotFound { + events: String::new(), + }; + for _ in 0..5 { + let res = get_contract(rb, tx_hash); + if res.is_ok() { + return res; + } + + let Some(err) = res.err() else { continue }; + last_error = err; + std::thread::sleep(std::time::Duration::from_secs(1)); + } + + Err(last_error) +} + +fn get_contract(rb: &ChainRequestBuilder, tx_hash: &str) -> Result { + let res = rb.query_tx_hash(tx_hash); + + let code = res["code"].as_i64().unwrap_or_default(); + if code != 0 { + let raw = res["raw_log"].as_str().unwrap_or_default(); + return Err(LocalError::TxNotSuccessful { + code_status: code, + raw_log: raw.to_string(), + }); + } + + for event in res["logs"][0]["events"].as_array().iter() { + for attr in event.iter() { + for attr_values in attr["attributes"].as_array().iter() { + for attr in attr_values.iter() { + if let Some(key) = attr["key"].as_str() { + if key.contains("contract_address") { + let contract_address = attr["value"].as_str().unwrap_or_default(); + return Ok(contract_address.to_string()); + } + } + } + } + } + } + + Err(LocalError::ContractAddressNotFound { + events: res.to_string(), + }) +} diff --git a/local-interchain/rust/localic-std/src/modules/gov.rs b/local-interchain/rust/localic-std/src/modules/gov.rs new file mode 100644 index 000000000..4d4e57092 --- /dev/null +++ b/local-interchain/rust/localic-std/src/modules/gov.rs @@ -0,0 +1,9 @@ +use serde_json::Value; + +use crate::transactions::ChainRequestBuilder; + +#[must_use] +pub fn get_proposal(req_builder: &ChainRequestBuilder, proposal_id: u64) -> Value { + let cmd = format!("q gov proposal {proposal_id}"); + req_builder.query(cmd.as_str(), false) +} diff --git a/local-interchain/rust/localic-std/src/modules/mod.rs b/local-interchain/rust/localic-std/src/modules/mod.rs new file mode 100644 index 000000000..2367b7115 --- /dev/null +++ b/local-interchain/rust/localic-std/src/modules/mod.rs @@ -0,0 +1,3 @@ +pub mod bank; +pub mod cosmwasm; +pub mod gov; diff --git a/local-interchain/rust/localic-std/src/node.rs b/local-interchain/rust/localic-std/src/node.rs new file mode 100644 index 000000000..0f2fc0236 --- /dev/null +++ b/local-interchain/rust/localic-std/src/node.rs @@ -0,0 +1,196 @@ +// ref: chain_node.go +use crate::{errors::LocalError, transactions::ChainRequestBuilder, types::RequestType}; +use serde_json::{json, Value}; + +// TODO: +// pub fn VoteOnProposal(rb: &ChainRequestBuilder, proposal_id: String, vote: String) -> String { +// pub fn SubmitProposal(key_name: String, proposal_json_path: Pathbuf) +// pub fn UpgradeProposal(key_name: String, upgradeheight, title, description, deposit) +// pub fn UnsafeResetAll(rb: &ChainRequestBuilder) -> String { + +#[derive(Clone)] +pub struct Chain<'a> { + rb: &'a ChainRequestBuilder, +} + +impl Chain<'_> { + #[must_use] + pub fn new(rb: &ChainRequestBuilder) -> Chain { + Chain { rb } + } + + fn info_builder(&self, request: &str, extra_params: Option<&[(&str, &str)]>) -> String { + let query_params: Vec<(&str, &str)> = { + let mut params = vec![ + ("chain_id", self.rb.chain_id.as_str()), + ("request", request), + ]; + + if let Some(extra_params) = extra_params { + params.extend_from_slice(extra_params); + } + + params + }; + let query_params: &[(&str, &str)] = query_params.as_slice(); + + let res = self + .rb + .client + .get(format!("{}/info", self.rb.api).as_str()) + .query(query_params) + .send() + .unwrap() + .text() + .unwrap(); + res + } + + // actions + #[must_use] + pub fn recover_key(&self, key_name: &str, mnemonic: &str) -> Value { + let cmd = format!("keyname={key_name};mnemonic={mnemonic}"); + self.rb + .send_request(RequestType::RecoverKey, cmd.as_str(), false) + } + + #[must_use] + pub fn overwrite_genesis_file(&self, content: &str) -> Value { + let cmd = format!("new_genesis={content}"); + self.rb + .send_request(RequestType::OverwriteGenesisFile, cmd.as_str(), false) + } + + #[must_use] + pub fn dump_contract_state(&self, contract_addr: &str, height: u64) -> Value { + let cmd = format!("contract={contract_addr};height={height}"); + self.rb + .send_request(RequestType::DumpContractState, cmd.as_str(), false) + } + + #[must_use] + pub fn set_peers(&self, peers: &str) -> Value { + let cmd = format!("new_peers={peers}"); + self.rb + .send_request(RequestType::SetNewPeers, cmd.as_str(), false) + } + + /// `add_full_node` adds a full node to the network. A full node must already be running on the same network. + #[must_use] + pub fn add_full_node(&self, amount: u64) -> Value { + let cmd = format!("amount={amount}"); + self.rb + .send_request(RequestType::AddFullNodes, cmd.as_str(), false) + } + + /// # Errors + /// + /// Returns `Err` if the key bech32 fails on the node. + pub fn account_key_bech_32(&self, key_name: &str) -> Result { + self.key_bech32(key_name, "") + } + + /// # Errors + /// + /// Returns `Err` if the key bech32 fails on the node. + pub fn key_bech32(&self, key_name: &str, bech_prefix: &str) -> Result { + let mut cmd = + format!("keys show --address {key_name} --home=%HOME% --keyring-backend=test"); + + if !bech_prefix.is_empty() { + cmd = format!("{cmd} --bech {bech_prefix}"); + } + + let res = &self.rb.bin(cmd.as_str(), true); + let text = res["text"].to_string(); + if text.contains("Error:") { + return Err(LocalError::KeyBech32Failed { + reason: res["text"].to_string(), + }); + } + + if !text.is_empty() { + let text = text.replace("\\n", ""); + let text = text.replace('\"', ""); + return Ok(text); + } + + Err(LocalError::KeyBech32Failed { + reason: res["error"].to_string(), + }) + } + + #[must_use] + pub fn get_chain_config(&self) -> Value { + let res = self.info_builder("config", None); + + match serde_json::from_str::(&res) { + Ok(res) => res, + Err(_) => { + json!({}) + } + } + } + + #[must_use] + pub fn get_name(&self) -> String { + self.info_builder("name", None) + } + + #[must_use] + pub fn get_container_id(&self) -> String { + self.info_builder("container_id", None) + } + + #[must_use] + pub fn get_host_name(&self) -> String { + self.info_builder("hostname", None) + } + + #[must_use] + pub fn get_genesis_file_content(&self) -> Option { + match self.info_builder("genesis_file_content", None).as_str() { + "" => None, + res => Some(res.to_string()), + } + } + + #[must_use] + pub fn get_home_dir(&self) -> String { + self.info_builder("home_dir", None) + } + + #[must_use] + pub fn get_height(&self) -> u64 { + let res = self.info_builder("height", None); + res.parse::().unwrap_or(0) + } + + #[must_use] + pub fn read_file(&self, relative_path: &str) -> String { + self.info_builder("read_file", Some(&[("relative_path", relative_path)])) + } + + #[must_use] + pub fn is_above_sdk_v47(&self) -> bool { + let res = self.info_builder("is_above_sdk_v47", None); + res.parse::().unwrap_or(false) + } + + #[must_use] + pub fn has_command(&self, command: &str) -> String { + self.info_builder("has_command", Some(&[("command", command)])) + } + + #[must_use] + pub fn get_build_information(&self) -> Value { + let res = self.info_builder("build_information", None); + + match serde_json::from_str::(&res) { + Ok(res) => res, + Err(_) => { + json!({}) + } + } + } +} diff --git a/local-interchain/rust/localic-std/src/polling.rs b/local-interchain/rust/localic-std/src/polling.rs new file mode 100644 index 000000000..70c2140fa --- /dev/null +++ b/local-interchain/rust/localic-std/src/polling.rs @@ -0,0 +1,21 @@ +use reqwest::blocking::Client as BClient; + +use crate::errors::LocalError; + +/// Polls the API URL for a response. If the response is successful, then the server is running. +/// # Errors +/// +/// If the server does not start within the `wait_seconds`, then an error is returned. +pub fn poll_for_start(c: &BClient, api_url: &str, wait_seconds: u32) -> Result<(), LocalError> { + for i in 0..wait_seconds { + if c.get(api_url).send().is_ok() { + return Ok(()); + } + println!("waiting for server to start (iter:{i}) ({api_url})"); + std::thread::sleep(std::time::Duration::from_secs(1)); + } + + Err(LocalError::ServerDidNotStart {}) +} + +// TODO: polling for a future block (wait_until) delta diff --git a/local-interchain/rust/localic-std/src/relayer.rs b/local-interchain/rust/localic-std/src/relayer.rs new file mode 100644 index 000000000..a509b5ab9 --- /dev/null +++ b/local-interchain/rust/localic-std/src/relayer.rs @@ -0,0 +1,166 @@ +use serde_json::Value; + +use crate::{errors::LocalError, transactions::ChainRequestBuilder}; + +#[derive(Clone)] +pub struct Relayer<'a> { + rb: &'a ChainRequestBuilder, +} + +#[derive(Clone, Debug)] +pub struct Channel { + pub channel_id: String, + pub connection_hops: Vec, + pub counterparty: Counterparty, + pub ordering: String, + pub port_id: String, + pub state: String, + pub version: String, +} + +#[derive(Clone, Debug)] +pub struct Counterparty { + pub channel_id: String, + pub port_id: String, +} + +impl Relayer<'_> { + #[must_use] + pub fn new(rb: &ChainRequestBuilder) -> Relayer { + Relayer { rb } + } + + /// # Errors + /// + /// Returns `Err` if the relayer is not able to execute the command. + pub fn execute(&self, cmd: &str, return_text: bool) -> Result { + let payload = serde_json::json!({ + "chain_id": self.rb.chain_id, + "action": "relayer-exec", + "cmd": cmd, + }); + + let res = self + .rb + .client + .post(&self.rb.api) + .json(&payload) + .header("Accept", "Content-Type: application/json") + .header("Content-Type", "Content-Type: application/json") + .send(); + + if return_text { + let res_text = match res { + Ok(res) => res.text().unwrap_or_default(), + Err(e) => return Err(LocalError::Custom { msg: e.to_string() }), + }; + + return Ok(serde_json::json!({ + "text": res_text, + })); + } + + match res { + Ok(res) => Ok(res.json().unwrap_or_default()), + Err(e) => Err(LocalError::Custom { msg: e.to_string() }), + } + } + + /// # Errors + /// + /// Returns `Err` if the relayer is not able to flush packets between 2 contracts. + pub fn flush(&self, path: &str, channel: &str) -> Result { + let cmd = format!("rly transact flush {path} {channel}"); + self.execute(cmd.as_str(), false) + } + + /// # Errors + /// + /// Returns `Err` if the relayer is not able to create the channel between 2 channels. + pub fn create_channel( + &self, + path: &str, + src: &str, + dst: &str, + order: &str, + version: &str, + ) -> Result { + let source: String = src.to_string(); + let destination: String = dst.to_string(); + + let cmd = format!( + "rly tx channel {path} --src-port {source} --dst-port {destination} --order {order} --version {version}", + ); + + self.execute(cmd.as_str(), false) + } + + /// # Errors + /// + /// Returns `Err` if the relayer is not able to return the channels. + /// + /// # Panics + /// + /// Panics if the relayer is not able to return the channels. + pub fn get_channels(&self, chain_id: &str) -> Result, LocalError> { + let payload = serde_json::json!({ + "chain_id": chain_id, + "action": "get_channels", + }); + + let res = self.rb.client.post(&self.rb.api).json(&payload).send(); + if let Err(res) = res { + return Err(LocalError::Custom { + msg: res.to_string(), + }); + } + + let channel_json: Value = match res { + Ok(res) => res.json().unwrap_or_default(), + Err(e) => return Err(LocalError::Custom { msg: e.to_string() }), + }; + + let mut channels: Vec = Vec::new(); + + let Some(c) = channel_json.as_array() else { + return Err(LocalError::Custom { + msg: "channel_json is not an array".to_string(), + }) + }; + + for channel in c { + let channel_id = channel["channel_id"] + .as_str() + .unwrap_or_default() + .to_string(); + let connection_hops = channel["connection_hops"].as_array().unwrap(); + let mut hops: Vec = Vec::new(); + for hop in connection_hops { + hops.push(hop.as_str().unwrap().to_string()); + } + let counterparty = channel["counterparty"].as_object().unwrap(); + let counterparty_channel_id = counterparty["channel_id"].as_str().unwrap().to_string(); + let counterparty_port_id = counterparty["port_id"].as_str().unwrap().to_string(); + let counterparty = Counterparty { + channel_id: counterparty_channel_id, + port_id: counterparty_port_id, + }; + let ordering = channel["ordering"].as_str().unwrap().to_string(); + let port_id = channel["port_id"].as_str().unwrap().to_string(); + let state = channel["state"].as_str().unwrap().to_string(); + let version = channel["version"].as_str().unwrap().to_string(); + let channel = Channel { + channel_id, + connection_hops: hops, + counterparty, + ordering, + port_id, + state, + version, + }; + channels.push(channel); + } + + Ok(channels) + } +} diff --git a/local-interchain/rust/localic-std/src/transactions.rs b/local-interchain/rust/localic-std/src/transactions.rs new file mode 100644 index 000000000..3b0bd57e7 --- /dev/null +++ b/local-interchain/rust/localic-std/src/transactions.rs @@ -0,0 +1,385 @@ +use std::path::Path; + +use reqwest::blocking::{Client, RequestBuilder}; +use serde_json::Value; + +use crate::{ + errors::LocalError, + types::{ActionHandler, RequestType}, +}; + +#[derive(Debug)] +pub struct ChainRequestBuilder { + pub client: Client, + pub api: String, + pub chain_id: String, + log_output: bool, +} + +impl ChainRequestBuilder { + /// # Errors + /// + /// Returns `Err` if the `api` or `chain_id` is empty. + pub fn new( + api: String, + chain_id: String, + log_output: bool, + ) -> Result { + if api.is_empty() { + return Err(LocalError::ApiNotFound {}); + } + if chain_id.is_empty() { + return Err(LocalError::ChainIdNotFound {}); + } + + Ok(ChainRequestBuilder { + client: Client::new(), + api, + chain_id, + log_output, + }) + } + + // app binary commands + #[must_use] + pub fn binary(&self, cmd: &str, return_text: bool) -> Value { + self.send_request(RequestType::Bin, cmd, return_text) + } + #[must_use] + pub fn bin(&self, cmd: &str, return_text: bool) -> Value { + self.binary(cmd, return_text) + } + + // app query commands + #[must_use] + pub fn query(&self, cmd: &str, return_text: bool) -> Value { + self.send_request(RequestType::Query, cmd, return_text) + } + #[must_use] + pub fn q(&self, cmd: &str, return_text: bool) -> Value { + self.query(cmd, return_text) + } + + // container execution commands + #[must_use] + pub fn execute(&self, cmd: &str, return_text: bool) -> Value { + self.send_request(RequestType::Exec, cmd, return_text) + } + #[must_use] + pub fn exec(&self, cmd: &str, return_text: bool) -> Value { + self.execute(cmd, return_text) + } + + /// # Errors + /// + /// Returns `Err` if the transaction fails. + pub fn transaction(&self, cmd: &str, get_data: bool) -> Result { + let command = set_missing_required_args( + cmd, + vec![ + "--node=%RPC%", + "--chain-id=%CHAIN_ID%", + "--keyring-backend=test", + "--home=%HOME%", + ], + ); + + let command = if command.contains("--yes") { + command + } else { + format!("{command} --yes") + }; + + let res = self.binary(command.as_str(), false); + if !get_data { + return Ok(res); + } + + let Some(tx_hash) = self.get_tx_hash(&res) else { return Err(LocalError::TxHashNotFound {}) }; + + for _ in 0..5 { + let data = self.query_tx_hash(&tx_hash); + + if !data.to_string().starts_with("{\"error\":") { + return Ok(data); + } + + std::thread::sleep(std::time::Duration::from_secs(1)); + } + + Err(LocalError::TxHashNotFound {}) + } + + /// # Errors + /// + /// Returns `Err` if the transaction fails. + pub fn tx(&self, cmd: &str, get_data: bool) -> Result { + self.transaction(cmd, get_data) + } + + #[must_use] + pub fn decode_transaction(&self, protobuf_bytes: &str, use_hex: bool) -> Value { + let mut cmd = format!("tx decode {protobuf_bytes}"); + if use_hex { + cmd = format!("{cmd} --hex"); + } + + self.binary(cmd.as_str(), false) + } + #[must_use] + pub fn get_tx_hash(&self, tx_res: &Value) -> Option { + let tx_hash = tx_res["txhash"].as_str(); + tx_hash.map(std::string::ToString::to_string) + } + + /// # Errors + /// + /// Returns `Err` if the transaction status code is not found. + pub fn get_sdk_status_code(&self, tx_res: &Value) -> Result { + let status_id = tx_res["code"].as_u64(); + match status_id { + Some(code) => Ok(code), + None => Err(LocalError::SdkTransactionStatusCodeNotFound { + reason: "'code' not found in Tx JSON response.".to_string(), + tx_res: tx_res.to_string(), + }), + } + } + + #[must_use] + pub fn get_raw_log(&self, tx_res: &Value) -> Option { + let raw_log = tx_res["raw_log"].as_str(); + let res = raw_log.map(std::string::ToString::to_string); + res.filter(|res| !(res.is_empty() || res == "[]")) + } + + #[must_use] + pub fn query_tx_hash(&self, tx_hash: &str) -> Value { + if tx_hash.is_empty() { + return serde_json::json!({ + "error": "tx_hash cannot be empty", + }); + } + + let cmd = format!("tx {tx_hash} --output=json"); + self.query(&cmd, false) + } + + /// # Errors + /// + /// Returns `Err` if the file does not exist or if the file path is not valid. + pub fn upload_file( + &self, + abs_path: &Path, + return_text: bool, + ) -> Result { + let file: String = match abs_path.to_str() { + Some(file) => file.to_string(), + None => { + return Err(LocalError::UploadFailed { + path: match abs_path.to_str() { + Some(path) => path.to_string(), + None => String::new(), + }, + reason: "file path is not valid".to_string(), + }); + } + }; + if !abs_path.exists() { + return Err(LocalError::UploadFailed { + path: file, + reason: "file does not exist".to_string(), + }); + } + + let payload = serde_json::json!({ + "chain_id": &self.chain_id, + "file_path": file, + }); + + let url = self.api.to_string(); + let url = if url.ends_with('/') { + url + "upload" + } else { + url + "/upload" + }; + + let header: &str = if return_text { + "Content-Type: text/plain" + } else { + "Content-Type: application/json" + }; + + Ok(self + .client + .post(url) + .json(&payload) + .header("Accept", header) + .header("Content-Type", header)) + } + + /// # Errors + /// + /// Returns `Err` if the contract is not uploaded. + pub fn upload_contract(&self, key_name: &str, abs_path: &Path) -> Result { + let Some(file) = abs_path.to_str() else { + return Err(LocalError::UploadFailed { + path: match abs_path.to_str() { + Some(path) => path.to_string(), + None => String::new(), + }, + reason: "file path is not valid".to_string(), + }); + }; + + let payload = serde_json::json!({ + "chain_id": &self.chain_id, + "file_path": file, + "key_name": key_name, + }); + + let req = self.upload_file(abs_path, false)?; + + let req = req.json(&payload).header("Upload-Type", "cosmwasm"); + + let resp = match req.send() { + Ok(resp) => resp, + Err(err) => { + return Err(LocalError::UploadFailed { + path: file.to_string(), + reason: err.to_string(), + }); + } + }; + match resp.text() { + Ok(text) => { + if text.contains("error") { + return Err(LocalError::UploadFailed { + path: file.to_string(), + reason: text, + }); + } + + // convert text to json + let json = match serde_json::from_str::(&text) { + Ok(json) => json, + Err(err) => { + return Err(LocalError::UploadFailed { + path: file.to_string(), + reason: err.to_string(), + }); + } + }; + + // get code_id from json + let Some(code_id) = json["code_id"].as_u64() else { + return Err(LocalError::UploadFailed { + path: file.to_string(), + reason: "code_id not found".to_string(), + }); + }; + + // return code_id + Ok(code_id) + } + Err(err) => Err(LocalError::UploadFailed { + path: file.to_string(), + reason: err.to_string(), + }), + } + } + + #[must_use] + pub fn send_request( + &self, + request_type: RequestType, + command: &str, + return_text: bool, + ) -> Value { + let mut cmd: String = command.to_string(); + if request_type == RequestType::Query { + if cmd.to_lowercase().starts_with("query ") { + cmd = cmd[6..].to_string(); + } else if cmd.to_lowercase().starts_with("q ") { + cmd = cmd[2..].to_string(); + } + }; + + // return JSON if we we did not override that. + if !return_text && (request_type == RequestType::Query || request_type == RequestType::Bin) + { + cmd = set_missing_required_args( + cmd.as_str(), + vec!["--node=%RPC%", "--chain-id=%CHAIN_ID%", "--output=json"], + ); + } + + // Build the request payload + let payload = ActionHandler::new(self.chain_id.clone(), request_type, cmd).to_json(); + + if self.log_output { + println!("[send_request payload]: {payload}"); + } + + let mut rb = self.client.post(&self.api).json(&payload); + + let content_type = if return_text { + "text/plain" + } else { + "application/json" + }; + + rb = rb + .header("Content-Type", content_type) + .header("Accept", content_type); + + let res = match rb.send() { + Ok(res) => res, + Err(err) => { + return return_text_json("", Some(err.to_string())); + } + }; + let text = res.text(); + + if return_text { + return return_text_json(text.unwrap_or_default().as_str(), None); + } + + match text { + Ok(text) => match serde_json::from_str::(&text) { + Ok(json) => json, + Err(err) => return_text_json(text.as_str(), Some(err.to_string())), + }, + Err(err) => return_text_json("", Some(err.to_string())), + } + } +} + +fn return_text_json(text: &str, err: Option) -> Value { + serde_json::json!({ + "text": &text, + "error": match err.unwrap_or_default() { + err if err.is_empty() => None, + err => Some(err), + }, + }) +} + +/// Sets missing command arguments that are required (such as the chain-id, keyring-backend, node, etc). +/// `required` elements must contain an '=' sign to be valid. +fn set_missing_required_args(cmd: &str, required: Vec<&str>) -> String { + let mut command = cmd.to_string(); + + for arg in required { + let parsed_args: String = match arg.split_once('=') { + Some((a, b)) => format!("{a} {b}"), + None => panic!("{arg} must have an '=' sign to be valie in the `required` argument"), + }; + + if !command.contains(arg) && !command.contains(parsed_args.as_str()) { + command = format!("{command} {arg}"); + } + } + + command +} diff --git a/local-interchain/rust/localic-std/src/types.rs b/local-interchain/rust/localic-std/src/types.rs new file mode 100644 index 000000000..2519bc316 --- /dev/null +++ b/local-interchain/rust/localic-std/src/types.rs @@ -0,0 +1,131 @@ +use std::fmt; + +use cosmwasm_std::{Coin, Uint128}; +use reqwest::blocking::Client; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum RequestType { + Bin, + Query, + Exec, + // custom + RecoverKey, + OverwriteGenesisFile, + SetNewPeers, + AddFullNodes, + DumpContractState, +} +impl RequestType { + #[must_use] + pub fn as_str(&self) -> &'static str { + // match handlers/actions.go + match self { + RequestType::Bin => "bin", + RequestType::Query => "query", + RequestType::Exec => "exec", + RequestType::RecoverKey => "recover-key", + RequestType::OverwriteGenesisFile => "overwrite-genesis-file", + RequestType::SetNewPeers => "set-peers", + RequestType::AddFullNodes => "add-full-nodes", + RequestType::DumpContractState => "dump-contract-state", + } + } +} + +#[derive(Debug, Clone)] +pub struct RequestBase { + pub client: Client, + pub url: String, + pub chain_id: String, + pub request_type: RequestType, +} + +impl RequestBase { + #[must_use] + pub fn new( + client: Client, + url: String, + chain_id: String, + request_type: RequestType, + ) -> RequestBase { + RequestBase { + client, + url, + chain_id, + request_type, + } + } +} + +pub struct ActionHandler { + pub chain_id: String, + pub action: RequestType, + pub cmd: String, +} + +impl ActionHandler { + #[must_use] + pub fn new(chain_id: String, action: RequestType, cmd: String) -> ActionHandler { + ActionHandler { + chain_id, + action, + cmd, + } + } + + #[must_use] + pub fn to_json_str(&self) -> String { + let escaped_cmd = self.cmd.replace('\"', "\\\""); + + let json = format!( + r#"{{"chain_id":"{}","action":"{}","cmd":"{}"}}"#, + self.chain_id, + self.action.as_str(), + escaped_cmd + ); + json + } + + #[must_use] + pub fn to_json(&self) -> serde_json::Value { + let json = self.to_json_str(); + let json: serde_json::Value = serde_json::from_str(&json).unwrap_or_default(); + json + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TransactionResponse { + pub status_code: u64, + pub tx_hash: Option, + pub raw_log: Option, +} + +impl fmt::Display for TransactionResponse { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "status: {:?} tx_hash: {:?}, rawlog: {:?}", + self.status_code, self.tx_hash, self.raw_log + ) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Contract { + pub address: String, + pub tx_hash: String, + pub admin: Option, +} + +#[must_use] +pub fn get_coin_from_json(value: &serde_json::Value) -> Coin { + let amount = value["amount"].as_str().unwrap_or_default(); + let denom = value["denom"].as_str().unwrap_or_default(); + let amount = amount.parse::().unwrap_or_default(); + + Coin { + denom: denom.to_string(), + amount, + } +} diff --git a/local-interchain/rust/main/Cargo.toml b/local-interchain/rust/main/Cargo.toml new file mode 100644 index 000000000..2b37eb9bf --- /dev/null +++ b/local-interchain/rust/main/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "localic-bin" +version = "0.0.1" +edition = "2021" +authors = ["Strangelove Developers"] + +[[bin]] +name = "localic-bin" +path = "src/main.rs" + +[dependencies] +reqwest.workspace = true +tokio.workspace = true +serde_json.workspace = true +cosmwasm-std.workspace = true +thiserror.workspace = true + +localic-std.workspace = true \ No newline at end of file diff --git a/local-interchain/rust/main/src/base.rs b/local-interchain/rust/main/src/base.rs new file mode 100644 index 000000000..c320c5cde --- /dev/null +++ b/local-interchain/rust/main/src/base.rs @@ -0,0 +1,41 @@ +use std::path; + +// Use clap to parse args in the future +pub const API_URL: &str = "http://127.0.0.1:8080"; + +/// local-interchain/rust directory +/// # Panics +/// +/// Will panic if the current directory path is not found. +#[must_use] +pub fn get_current_dir() -> path::PathBuf { + match std::env::current_dir() { + Ok(p) => p, + Err(e) => panic!("Could not get current dir: {e}"), + } +} + +/// local-interchain directory +/// # Panics +/// +/// Will panic if the `local_interchain` directory is not found in the parent path. +#[must_use] +pub fn get_local_interchain_dir() -> path::PathBuf { + let current_dir = get_current_dir(); + let Some(parent_dir) = current_dir.parent() else { panic!("Could not get parent dir") }; + parent_dir.to_path_buf() +} + +/// local-interchain/contracts directory +#[must_use] +pub fn get_contract_path() -> path::PathBuf { + get_local_interchain_dir().join("contracts") +} + +/// local-interchain/configs/contract.json file +#[must_use] +pub fn get_contract_cache_path() -> path::PathBuf { + get_local_interchain_dir() + .join("configs") + .join("contract.json") +} diff --git a/local-interchain/rust/main/src/main.rs b/local-interchain/rust/main/src/main.rs new file mode 100644 index 000000000..b1836bc93 --- /dev/null +++ b/local-interchain/rust/main/src/main.rs @@ -0,0 +1,314 @@ +#![allow(dead_code, unused_must_use)] + +// Import base libraries +use cosmwasm_std::Coin; +use cosmwasm_std::Uint128; +use reqwest::blocking::Client; +use serde_json::json; + +// Import Local-Interchain std library methods +use localic_std::filesystem::get_files; +use localic_std::node::Chain; +use localic_std::polling::poll_for_start; +use localic_std::relayer::Relayer; +use localic_std::transactions::ChainRequestBuilder; + +// Import Local-Interchain SDK modules +use localic_std::modules::bank::{get_balance, get_total_supply, send}; +use localic_std::modules::cosmwasm::CosmWasm; + +// base helpers for this binary +pub mod base; +use base::{ + get_contract_cache_path, get_contract_path, get_current_dir, get_local_interchain_dir, API_URL, +}; + +// local-ic start juno_ibc +// cargo run --package localic-bin --bin localic-bin +fn main() { + poll_for_start(&Client::new(), API_URL, 150); + + let rb: ChainRequestBuilder = + match ChainRequestBuilder::new(API_URL.to_string(), "localjuno-1".to_string(), true) { + Ok(rb) => rb, + Err(err) => { + panic!("ChainRequestBuilder failed: {err:?}"); + } + }; + let node_a: Chain = Chain::new(&rb); + + let rb2: ChainRequestBuilder = + match ChainRequestBuilder::new(API_URL.to_string(), "localjuno-2".to_string(), true) { + Ok(rb) => rb, + Err(err) => { + panic!("ChainRequestBuilder failed: {err:?}"); + } + }; + + test_paths(&rb); + test_queries(&rb); + test_binary(&rb); + test_bank_send(&rb); + + test_ibc_contract_relaying(&node_a, &rb, &rb2); + test_node_information(&node_a); + test_node_actions(&node_a); +} + +fn test_ibc_contract_relaying(node: &Chain, rb1: &ChainRequestBuilder, rb2: &ChainRequestBuilder) { + // local-ic start juno_ibc + let file_path = get_contract_path().join("cw_ibc_example.wasm"); + let key1 = "acc0"; + let key2 = "second0"; + + let relayer = Relayer::new(rb2); + + let mut contract_a = CosmWasm::new(rb1); + let mut contract_b = CosmWasm::new(rb2); + + let c1_store = contract_a.store(key1, &file_path); + let c2_store = contract_b.store(key2, &file_path); + assert_eq!( + c1_store.unwrap_or_default(), + contract_a.code_id.unwrap_or_default() + ); + assert_eq!( + c2_store.unwrap_or_default(), + contract_b.code_id.unwrap_or_default() + ); + + let ca = contract_a.instantiate(key1, "{}", "contractA", None, ""); + let cb = contract_b.instantiate(key2, "{}", "contractB", None, ""); + println!("contract_a: {ca:?}"); + println!("contract_b: {cb:?}"); + + // example: manual relayer connection + // let wc = relayer.create_channel( + // "juno-ibc-1", + // format!("wasm.{}", &contract_a.contract_addr.as_ref().unwrap()).as_str(), + // format!("wasm.{}", &contract_b.contract_addr.as_ref().unwrap()).as_str(), + // "unordered", + // "counter-1", + // ); + + contract_a.create_wasm_connection( + &relayer, + "juno-ibc-1", + &contract_b, + "unordered", + "counter-1", + ); + + let channels = relayer.get_channels(rb1.chain_id.as_str()); + println!("channels: {channels:?}"); + + let channel_id = "channel-1"; + + // then execute on the contract + let res = contract_b.execute( + key2, + json!({"increment":{"channel":channel_id}}) + .to_string() + .as_str(), + "--gas-adjustment=3.0", + ); + println!("\ncw2.execute_contract: {res:?}"); + + // flush packets + println!( + "relayer.flush: {:?}", + relayer.flush("juno-ibc-1", channel_id) + ); + + let query_res = contract_a.query_value(&json!({"get_count":{"channel":channel_id}})); + println!("\nquery_res: {query_res:?}"); + assert_eq!(query_res, serde_json::json!({"data":{"count":1}})); + + // dump the contracts state to JSON + let height = node.get_height(); + let dump_res = node.dump_contract_state(&contract_a.contract_addr.as_ref().unwrap(), height); + println!("dump_res: {dump_res:?}"); +} + +fn test_node_actions(node: &Chain) { + let keyname = "abc"; + let words = "offer excite scare peanut rally speak suggest unit reflect whale cloth speak joy unusual wink session effort hidden angry envelope click race allow buffalo"; + let expected_addr = "juno1cp8wps50zemt3x5tn3sgqh3x93rlt8cw6tkgx4"; + + let res = node.recover_key(keyname, words); + println!("res: {res:?}"); + + let acc = node.account_key_bech_32("abc"); + println!("acc: {acc:?}"); + assert_eq!(acc.unwrap_or_default(), expected_addr); + + let res = node.overwrite_genesis_file(r#"{"test":{}}"#); + println!("res: {res:?}"); + node.get_genesis_file_content(); // verify this is updated + + // TODO: keep this disabled for now. The chain must already have a full node running to not err. + // let res = node.add_full_node(1); + // println!("res: {:?}", res); +} + +fn test_node_information(node: &Chain) { + let v = node.account_key_bech_32("acc0"); + assert_eq!( + v.unwrap_or_default(), + "juno1hj5fveer5cjtn4wd6wstzugjfdxzl0xps73ftl" + ); + + let v = node.account_key_bech_32("fake-key987"); + assert!(v.is_err()); + + node.get_chain_config(); + + assert!(node.get_name().starts_with("localjuno-1-val-0")); + node.get_container_id(); + node.get_host_name(); + node.get_genesis_file_content(); + node.get_home_dir(); + node.get_height(); + node.read_file("./config/app.toml"); + node.is_above_sdk_v47(); + node.has_command("genesis"); // false with sdk 45 + node.has_command("tx"); // every bin has this + let res = node.get_build_information(); // every bin has this + println!( + "res: {}", + res["cosmos_sdk_version"].as_str().unwrap_or_default() + ); + + // TODO: test: + // get_proposal(rb, "1"); +} + +fn test_paths(rb: &ChainRequestBuilder) { + println!("current_dir: {:?}", get_current_dir()); + println!("local_interchain_dir: {:?}", get_local_interchain_dir()); + println!("contract_path: {:?}", get_contract_path()); + println!("contract_json_path: {:?}", get_contract_cache_path()); + + // upload Makefile to the chain's home dir + let arb_file = get_current_dir().join("Makefile"); + match rb.upload_file(&arb_file, true) { + Ok(rb) => { + let res = match rb.send() { + Ok(r) => r, + Err(err) => { + panic!("upload_file failed on request send {err:?}"); + } + }; + let body = match res.text() { + Ok(body) => body, + Err(err) => { + panic!("upload_file failed on response body {err:?}"); + } + }; + println!("body: {body:?}"); + assert_eq!(body, "{\"success\":\"file uploaded to localjuno-1\",\"location\":\"/var/cosmos-chain/localjuno-1/Makefile\"}"); + } + Err(err) => { + panic!("upload_file failed {err:?}"); + } + }; + + let files = match get_files(rb, "/var/cosmos-chain/localjuno-1") { + Ok(files) => files, + Err(err) => { + panic!("get_files failed {err:?}"); + } + }; + + assert!(files.contains(&"Makefile".to_string())); + assert!(files.contains(&"config".to_string())); + assert!(files.contains(&"data".to_string())); + assert!(files.contains(&"keyring-test".to_string())); + println!("files: {files:?}"); +} + +fn test_bank_send(rb: &ChainRequestBuilder) { + let before_bal = get_balance(rb, "juno10r39fueph9fq7a6lgswu4zdsg8t3gxlq670lt0"); + + let res = send( + rb, + "acc0", + "juno10r39fueph9fq7a6lgswu4zdsg8t3gxlq670lt0", + &[Coin { + denom: "ujuno".to_string(), + amount: Uint128::new(5), + }], + &Coin { + denom: "ujuno".to_string(), + amount: Uint128::new(5000), + }, + ); + match res { + Ok(res) => { + println!("res: {res}"); + } + Err(err) => { + println!("err: {err}"); + } + } + + let after_amount = get_balance(rb, "juno10r39fueph9fq7a6lgswu4zdsg8t3gxlq670lt0"); + + println!("before: {before_bal:?}"); + println!("after: {after_amount:?}"); +} + +fn test_queries(rb: &ChainRequestBuilder) { + test_all_accounts(rb); + let c = get_total_supply(rb); + println!("total supply: {c:?}"); +} +fn test_binary(rb: &ChainRequestBuilder) { + rb.binary("config", false); + get_keyring_accounts(rb); + + let decoded = rb.decode_transaction("ClMKUQobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjIIpwISK2p1bm8xZGM3a2MyZzVrZ2wycmdmZHllZGZ6MDl1YTlwZWo1eDNsODc3ZzcYARJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECxjGMmYp4MlxxfFWi9x4u+jOleJVde3Cru+HnxAVUJmgSBAoCCH8YNBISCgwKBXVqdW5vEgMyMDQQofwEGkDPE4dCQ4zUh6LIB9wqNXDBx+nMKtg0tEGiIYEH8xlw4H8dDQQStgAe6xFO7I/oYVSWwa2d9qUjs9qyB8r+V0Gy", false); + println!("decoded: {decoded:?}"); +} + +fn test_all_accounts(rb: &ChainRequestBuilder) { + let res = rb.query("q auth accounts", false); + println!("res: {res}"); + + let Some(accounts) = res["accounts"].as_array() else { + println!("No accounts found."); + return; + }; + + for account in accounts.iter() { + let acc_type = account["@type"].as_str().unwrap_or_default(); + + let addr: &str = match acc_type { + // "/cosmos.auth.v1beta1.ModuleAccount" => account["base_account"]["address"] + "/cosmos.auth.v1beta1.ModuleAccount" => account.get("base_account").unwrap()["address"] + .as_str() + .unwrap_or_default(), + _ => account["address"].as_str().unwrap_or_default(), + }; + + println!("{acc_type}: {addr}"); + } +} + +fn get_keyring_accounts(rb: &ChainRequestBuilder) { + let accounts = rb.binary("keys list --keyring-backend=test", false); + + let addrs = accounts["addresses"].as_array(); + addrs.map_or_else( + || { + println!("No accounts found."); + }, + |addrs| { + for acc in addrs.iter() { + let name = acc["name"].as_str().unwrap_or_default(); + let address = acc["address"].as_str().unwrap_or_default(); + println!("Key '{name}': {address}"); + } + }, + ); +} diff --git a/local-interchain/scripts/helpers/cosmwasm.py b/local-interchain/scripts/helpers/cosmwasm.py index 79716b8b7..2d27831cf 100644 --- a/local-interchain/scripts/helpers/cosmwasm.py +++ b/local-interchain/scripts/helpers/cosmwasm.py @@ -11,13 +11,13 @@ contracts_storage_dir = os.path.join(root_dir, "contracts") -def upload_file(rb: RequestBuilder, key_name: str, abs_path: str) -> dict: +def upload_contract(rb: RequestBuilder, key_name: str, abs_path: str) -> dict: print(f"[upload_file] ({rb.chain_id}) {abs_path}") payload = { "chain_id": rb.chain_id, "key_name": key_name, - "file_name": abs_path, + "file_path": abs_path, } url = rb.api @@ -26,10 +26,11 @@ def upload_file(rb: RequestBuilder, key_name: str, abs_path: str) -> dict: else: url += "/upload" + # Setting "Upload-Type": "cosmwasm" uploads the file and stores it on the chain. res = post( url, json=payload, - headers={"Content-Type": "application/json"}, + headers={"Content-Type": "application/json", "Upload-Type": "cosmwasm"}, timeout=120, ) @@ -74,7 +75,7 @@ def store_contract(self, key_name: str, abs_path: str) -> "CosmWasm": print(f"[Cache] CodeID={self.code_id} for {sub_file_path}") return self - res = upload_file(self.rb, key_name, abs_path) + res = upload_contract(self.rb, key_name, abs_path) if "error" in res: raise Exception(res["error"])