From 2daca7937cfd3ce25414298f0912d2227684f907 Mon Sep 17 00:00:00 2001 From: Chad Ostrowski <221614+chadoh@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:03:11 -0400 Subject: [PATCH] fix: nonInvokerSigningBy contracts Fixes: #1030 > `needsNonInvokerSigningBy()`'s `filter()` condition returns authorization entires whose address.signature value is `scvVoid`. In my case, this address was a contract. I was trying to figure out how to sign a transaction that used custom auth. > > However, the returned list is then mapped through a function that assumes all addresses are stellar account public keys. This lead to a `TypeError: accountId not set` error. > > **To Reproduce** > > Use the following transaction to construct an `AssembledTransaction` and call `needsNonInvokerSigningBy()`: > > ``` > AAAAAgAAAADhY+75HJcYjxA07MhDzZk/DwQzVITe9slCwDgcEGcc+AACfJ8AEJEBAAAAAwAAAAEAAAAAAAAAAAAAAABmv+l8AAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABOSACpv7RbyYK4XnWgj9AJ/GjrIPjLEoWJa/Iqo1a//YAAAAEbWludAAAAAIAAAASAAAAAAAAAADhY+75HJcYjxA07MhDzZk/DwQzVITe9slCwDgcEGcc+AAAAAoAAAAAAAAAAAAAAAAAAABkAAAAAQAAAAEAAAABEGJ2U3ldj9dca1n1NK0i2XkfL/5zXZsxAOIxUmn9RWknYhAjSWxRgQAAAAAAAAABAAAAAAAAAAE5IAKm/tFvJgrhedaCP0An8aOsg+MsShYlr8iqjVr/9gAAAARtaW50AAAAAgAAABIAAAAAAAAAAOFj7vkclxiPEDTsyEPNmT8PBDNUhN72yULAOBwQZxz4AAAACgAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAEAAAAAAAAAAAagL+zRaDmKTHWcB67aFNUZNLVg6ojcyrbO1u3Sg/KcAAAAAYAAAABOSACpv7RbyYK4XnWgj9AJ/GjrIPjLEoWJa/Iqo1a//YAAAAUAAAAAQAAAAYAAAABwfs5RVshthImbX+wmqWS0t1AjL4y5EjMMtSk/oEy11AAAAAUAAAAAQAAAAeheXOfVamUQyN7XuqlXPfXnmxx6tJxnGFVIA2YxmGnMwAAAAIAAAABAAAAAOFj7vkclxiPEDTsyEPNmT8PBDNUhN72yULAOBwQZxz4AAAAAlRFU1Q0AAAAAAAAAAAAAAAagL+zRaDmKTHWcB67aFNUZNLVg6ojcyrbO1u3Sg/KcAAAAAYAAAABEGJ2U3ldj9dca1n1NK0i2XkfL/5zXZsxAOIxUmn9RWkAAAAVJ2IQI0lsUYEAAAAAABUmowAADXgAAADEAAAAAAACfDsAAAAA > ``` > > **Expected behavior** > > The function should not assume all auth entries addresses are stellar accounts -- it should also support contract addresses. also: - switch to new test cases from AhaLabs/soroban-test-contracts - switch to `stellar` instead of `soroban` - use latest cli version - this version fixes a problem that some of the tests were working around, so they were removed --- .cargo/config.toml | 2 +- .env | 6 +- src/contract/assembled_transaction.ts | 13 +- test/e2e/initialize.sh | 49 +++---- .../src/test-contract-client-constructor.js | 44 ++---- test/e2e/src/test-methods-as-args.js | 6 +- .../test-non-invoker-signing-by-contracts.js | 32 +++++ test/e2e/src/util.js | 134 +++++++----------- test/e2e/wasms/README.md | 23 ++- test/e2e/wasms/stellar_asset_admin.wasm | Bin 0 -> 723 bytes test/e2e/wasms/stellar_asset_wrapper.wasm | Bin 0 -> 2350 bytes test/e2e/wasms/test_custom_types.wasm | Bin 18002 -> 0 bytes test/e2e/wasms/test_hello_world.wasm | Bin 7323 -> 0 bytes test/e2e/wasms/test_swap.wasm | Bin 2025 -> 0 bytes test/e2e/wasms/test_token.wasm | Bin 7494 -> 0 bytes 15 files changed, 152 insertions(+), 157 deletions(-) create mode 100644 test/e2e/src/test-non-invoker-signing-by-contracts.js create mode 100644 test/e2e/wasms/stellar_asset_admin.wasm create mode 100644 test/e2e/wasms/stellar_asset_wrapper.wasm delete mode 100755 test/e2e/wasms/test_custom_types.wasm delete mode 100755 test/e2e/wasms/test_hello_world.wasm delete mode 100755 test/e2e/wasms/test_swap.wasm delete mode 100755 test/e2e/wasms/test_token.wasm diff --git a/.cargo/config.toml b/.cargo/config.toml index 51ef227e4..3aa25973a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,7 +1,7 @@ # paths = ["/path/to/override"] # path dependency overrides [alias] # command aliases -install_soroban = "install --version 21.4.1 --root ./target soroban-cli --debug" +install_stellar = "install --version 21.5.0 --root ./target stellar-cli --debug --force" # b = "build --target wasm32-unknown-unknown --release" # c = "check" # t = "test" diff --git a/.env b/.env index 6516b6c9c..efc8a0743 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -SOROBAN_NETWORK_PASSPHRASE="Standalone Network ; February 2017" -SOROBAN_RPC_URL="http://localhost:8000/soroban/rpc" -SOROBAN_ACCOUNT="me" +STELLAR_NETWORK_PASSPHRASE="Standalone Network ; February 2017" +STELLAR_RPC_URL="http://localhost:8000/soroban/rpc" +STELLAR_ACCOUNT="me" FRIENDBOT_URL="http://localhost:8000/friendbot" diff --git a/src/contract/assembled_transaction.ts b/src/contract/assembled_transaction.ts index ffaa0f060..b4e909be3 100644 --- a/src/contract/assembled_transaction.ts +++ b/src/contract/assembled_transaction.ts @@ -2,6 +2,7 @@ /* eslint max-classes-per-file: 0 */ import { Account, + Address, BASE_FEE, Contract, Operation, @@ -636,9 +637,11 @@ export class AssembledTransaction { ); } - if (this.needsNonInvokerSigningBy().length) { + // filter out contracts, as these are dealt with via cross contract calls + const sigsNeeded = this.needsNonInvokerSigningBy().filter(id => !id.startsWith('C')); + if (sigsNeeded.length) { throw new AssembledTransaction.Errors.NeedsMoreSignatures( - "Transaction requires more signatures. " + + `Transaction requires signatures from ${sigsNeeded}. ` + "See `needsNonInvokerSigningBy` for details.", ); } @@ -760,9 +763,9 @@ export class AssembledTransaction { "scvVoid"), ) .map((entry) => - StrKey.encodeEd25519PublicKey( - entry.credentials().address().address().accountId().ed25519(), - ), + Address.fromScAddress( + entry.credentials().address().address(), + ).toString(), ), ), ]; diff --git a/test/e2e/initialize.sh b/test/e2e/initialize.sh index cb471901c..9bb1e6b53 100755 --- a/test/e2e/initialize.sh +++ b/test/e2e/initialize.sh @@ -14,26 +14,26 @@ dirname="$(CDPATH= cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" echo "###################### Initializing e2e tests ########################" -soroban="$dirname/../../target/bin/soroban" -if [[ -f "$soroban" ]]; then - current=$($soroban --version | head -n 1 | cut -d ' ' -f 2) +stellar=$(realpath "$dirname/../../target/bin/stellar") +if [[ -f "$stellar" ]]; then + current=$($stellar --version | head -n 1 | cut -d ' ' -f 2) desired=$(cat .cargo/config.toml | grep -oE -- "--version\s+\S+" | awk '{print $2}') if [[ "$current" != "$desired" ]]; then - echo "Current pinned soroban binary: $current. Desired: $desired. Building soroban binary." - (cd "$dirname/../.." && cargo install_soroban) + echo "Current pinned stellar binary: $current. Desired: $desired. Building stellar binary." + (cd "$dirname/../.." && cargo install_stellar) else - echo "Using soroban binary from ./target/bin" + echo "Using stellar binary from ./target/bin" fi else - echo "Building pinned soroban binary" - (cd "$dirname/../.." && cargo install_soroban) + echo "Building pinned stellar binary" + (cd "$dirname/../.." && cargo install_stellar) fi -NETWORK_STATUS=$(curl -s -X POST "$SOROBAN_RPC_URL" -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 8675309, "method": "getHealth" }' | sed -n 's/.*"status":\s*"\([^"]*\)".*/\1/p') +NETWORK_STATUS=$(curl -s -X POST "$STELLAR_RPC_URL" -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 8675309, "method": "getHealth" }' | sed -n 's/.*"status":\s*"\([^"]*\)".*/\1/p') echo Network -echo " RPC: $SOROBAN_RPC_URL" -echo " Passphrase: \"$SOROBAN_NETWORK_PASSPHRASE\"" +echo " RPC: $STELLAR_RPC_URL" +echo " Passphrase: \"$STELLAR_NETWORK_PASSPHRASE\"" echo " Status: $NETWORK_STATUS" if [[ "$NETWORK_STATUS" != "healthy" ]]; then @@ -41,23 +41,24 @@ if [[ "$NETWORK_STATUS" != "healthy" ]]; then exit 1 fi -$soroban keys generate $SOROBAN_ACCOUNT +$stellar keys generate $STELLAR_ACCOUNT -# retrieve the contracts using soroban contract init then build them if they dont already exist +# retrieve the contracts using stellar contract init then build them if they dont already exist # Define directory and WASM file paths target_dir="$dirname/test-contracts/target/wasm32-unknown-unknown/release" contracts_dir="$dirname/test-contracts" -repo_url="https://github.com/stellar/soroban-examples.git" +test_examples_repo="https://github.com/AhaLabs/soroban-test-examples.git" wasm_files=( - "soroban_other_custom_types_contract.wasm" - "soroban_atomic_swap_contract.wasm" - "soroban_token_contract.wasm" - "soroban_increment_contract.wasm" - "hello_world.wasm" + "custom_types.wasm" + "atomic_swap.wasm" + "token.wasm" + "increment.wasm" + "needs_a_signature.wasm" + "this_one_signs.wasm" ) get_remote_git_hash() { - git ls-remote "$repo_url" HEAD | cut -f1 + git ls-remote "$test_examples_repo" HEAD | cut -f1 } # Get the current git hash @@ -75,7 +76,7 @@ fi all_exist=true for wasm_file in "${wasm_files[@]}"; do if [ ! -f "$target_dir/$wasm_file" ]; then - all_exist=false + all_exist=false break fi done @@ -84,11 +85,11 @@ done if [ "$all_exist" = false ] || [ "$current_hash" != "$stored_hash" ]; then echo "WASM files are missing or contracts have been updated. Initializing and building contracts..." # Initialize contracts - $soroban contract init "$dirname/test-contracts" --with-example increment other_custom_types atomic_swap token - + npx degit --force AhaLabs/soroban-test-examples "$dirname/test-contracts" + # Change directory to test-contracts and build the contracts cd "$dirname/test-contracts" || { echo "Failed to change directory!"; exit 1; } - $soroban contract build + $stellar contract build # Save git hash to file echo "$current_hash" > "$hash_file" else diff --git a/test/e2e/src/test-contract-client-constructor.js b/test/e2e/src/test-contract-client-constructor.js index 16756156e..b03d5ec11 100644 --- a/test/e2e/src/test-contract-client-constructor.js +++ b/test/e2e/src/test-contract-client-constructor.js @@ -5,6 +5,7 @@ const { networkPassphrase, rpcUrl, generateFundedKeypair, + run, } = require("./util"); const { Address, contract } = require("../../../lib"); @@ -15,47 +16,32 @@ async function clientFromConstructor( if (!contracts[name]) { throw new Error( `Contract ${name} not found. ` + - `Pick one of: ${Object.keys(contracts).join(", ")}`, + `Pick one of: ${Object.keys(contracts).join()}`, ); } keypair = await keypair; // eslint-disable-line no-param-reassign const wallet = contract.basicNodeSigner(keypair, networkPassphrase); const { path } = contracts[name]; - const xdr = JSON.parse( - spawnSync( - "./target/bin/soroban", - ["contract", "inspect", "--wasm", path, "--output", "xdr-base64-array"], - { shell: true, encoding: "utf8" }, - ).stdout.trim(), - ); + const inspected = run( + `./target/bin/stellar contract inspect --wasm ${path} --output xdr-base64-array`, + ).stdout; + const xdr = JSON.parse(inspected); const spec = new contract.Spec(xdr); let wasmHash = contracts[name].hash; if (!wasmHash) { - wasmHash = spawnSync( - "./target/bin/soroban", - ["contract", "install", "--wasm", path], - { shell: true, encoding: "utf8" }, - ).stdout.trim(); + wasmHash = run( + `./target/bin/stellar contract install --wasm ${path}`, + ).stdout; } // TODO: do this with js-stellar-sdk, instead of shelling out to the CLI contractId = contractId ?? - spawnSync( - "./target/bin/soroban", - [ - // eslint-disable-line no-param-reassign - "contract", - "deploy", - "--source", - keypair.secret(), - "--wasm-hash", - wasmHash, - ], - { shell: true, encoding: "utf8" }, - ).stdout.trim(); + run( + `./target/bin/stellar contract deploy --source ${keypair.secret()} --wasm-hash ${wasmHash}`, + ).stdout; const client = new contract.Client(spec, { networkPassphrase, @@ -92,15 +78,15 @@ async function clientForFromTest(contractId, publicKey, keypair) { describe("Client", function () { before(async function () { const { client, keypair, contractId } = - await clientFromConstructor("helloWorld"); + await clientFromConstructor("customTypes"); const publicKey = keypair.publicKey(); const addr = Address.fromString(publicKey); this.context = { client, publicKey, addr, contractId, keypair }; }); it("can be constructed with `new Client`", async function () { - const { result } = await this.context.client.hello({ to: "tests" }); - expect(result).to.deep.equal(["Hello", "tests"]); + const { result } = await this.context.client.hello({ hello: "tests" }); + expect(result).to.equal("tests"); }); it("can be constructed with `from`", async function () { diff --git a/test/e2e/src/test-methods-as-args.js b/test/e2e/src/test-methods-as-args.js index 97cf096c8..0d7007766 100644 --- a/test/e2e/src/test-methods-as-args.js +++ b/test/e2e/src/test-methods-as-args.js @@ -8,8 +8,8 @@ function callMethod(method, args) { describe("methods-as-args", function () { it("should pass methods as arguments and have them still work", async function () { - const { client } = await clientFor("helloWorld"); - const { result } = await callMethod(client.hello, { to: "tests" }); - expect(result).to.deep.equal(["Hello", "tests"]); + const { client } = await clientFor("customTypes"); + const { result } = await callMethod(client.hello, { hello: "tests" }); + expect(result).to.equal("tests"); }); }); diff --git a/test/e2e/src/test-non-invoker-signing-by-contracts.js b/test/e2e/src/test-non-invoker-signing-by-contracts.js new file mode 100644 index 000000000..704c2e28f --- /dev/null +++ b/test/e2e/src/test-non-invoker-signing-by-contracts.js @@ -0,0 +1,32 @@ +const { expect } = require("chai"); +const { clientFor } = require("./util"); + +before(async function () { + const { contractId: signingContractId, keypair } = + await clientFor("doesSigning"); + const { client: needsSignature } = await clientFor("needsSignature", { + keypair, + }); + + const tx = await needsSignature.hello({ + to: keypair.publicKey(), + sign_with: signingContractId, + }); + this.context = { tx, doesSigningId: signingContractId }; +}); + +describe("needsNonInvokerSigningBy", function () { + it("does not assume stellar accounts", async function () { + expect(this.context.tx.needsNonInvokerSigningBy()[0]).to.equal( + this.context.doesSigningId, + ); + }); +}); + +describe("sign", function () { + it("doesn't throw error when nonInvokerSigningBy returns a contract", async function () { + expect( + async () => await this.context.tx.sign({ force: true }), + ).to.not.throw(); + }); +}); diff --git a/test/e2e/src/util.js b/test/e2e/src/util.js index 090213ef7..fedf3fa15 100644 --- a/test/e2e/src/util.js +++ b/test/e2e/src/util.js @@ -1,67 +1,58 @@ const { spawnSync } = require("node:child_process"); const { contract, Keypair } = require("../../../lib"); +const path = require("node:path"); -const basePath = `${__dirname}/../test-contracts/target/wasm32-unknown-unknown/release`; +/* + * Run a Bash command, returning stdout, stderr, and status code. + */ +function run(command) { + const [cmd, ...args] = command.split(" "); + const result = spawnSync(cmd, args, { shell: true, encoding: "utf8" }); + return { + stdout: result.stdout.trim(), + stderr: result.stderr.trim(), + status: result.status, + }; +} +module.exports.run = run; + +const stellar = "./target/bin/stellar"; +const basePath = path.resolve( + `${__dirname}/../test-contracts/target/wasm32-unknown-unknown/release`, +); const contracts = { customTypes: { - hash: spawnSync( - "./target/bin/soroban", - [ - "contract", - "install", - "--wasm", - `${basePath}/soroban_other_custom_types_contract.wasm`, - ], - { shell: true, encoding: "utf8" }, - ).stdout.trim(), - path: `${basePath}/soroban_custom_types_contract.wasm`, - }, - helloWorld: { - hash: spawnSync( - "./target/bin/soroban", - ["contract", "install", "--wasm", `${basePath}/hello_world.wasm`], - { shell: true, encoding: "utf8" }, - ).stdout.trim(), - path: `${basePath}/hello_world.wasm`, + hash: run( + `${stellar} contract install --wasm ${basePath}/custom_types.wasm`, + ).stdout, + path: `${basePath}/custom_types.wasm`, }, increment: { - hash: spawnSync( - "./target/bin/soroban", - [ - "contract", - "install", - "--wasm", - `${basePath}/soroban_increment_contract.wasm`, - ], - { shell: true, encoding: "utf8" }, - ).stdout.trim(), - path: `${basePath}/soroban_increment_contract.wasm`, + hash: run(`${stellar} contract install --wasm ${basePath}/increment.wasm`) + .stdout, + path: `${basePath}/increment.wasm`, }, swap: { - hash: spawnSync( - "./target/bin/soroban", - [ - "contract", - "install", - "--wasm", - `${basePath}/soroban_atomic_swap_contract.wasm`, - ], - { shell: true, encoding: "utf8" }, - ).stdout.trim(), - path: `${basePath}/soroban_atomic_swap_contract.wasm`, + hash: run(`${stellar} contract install --wasm ${basePath}/atomic_swap.wasm`) + .stdout, + path: `${basePath}/atomic_swap.wasm`, }, token: { - hash: spawnSync( - "./target/bin/soroban", - [ - "contract", - "install", - "--wasm", - `${basePath}/soroban_token_contract.wasm`, - ], - { shell: true, encoding: "utf8" }, - ).stdout.trim(), - path: `${basePath}/soroban_token_contract.wasm`, + hash: run(`${stellar} contract install --wasm ${basePath}/token.wasm`) + .stdout, + path: `${basePath}/token.wasm`, + }, + needsSignature: { + hash: run( + `${stellar} contract install --wasm ${basePath}/needs_a_signature.wasm`, + ).stdout, + path: `${basePath}/needs_a_signature.wasm`, + }, + doesSigning: { + hash: run( + `${stellar} contract install --wasm ${basePath}/this_one_signs.wasm`, + ).stdout, + path: `${basePath}/this_one_signs.wasm`, }, }; module.exports.contracts = contracts; @@ -97,10 +88,7 @@ module.exports.generateFundedKeypair = generateFundedKeypair; * By default, will re-deploy the contract every time. Pass in the same * `contractId` again if you want to re-use the a contract instance. */ -async function clientFor( - name, - { keypair = generateFundedKeypair(), contractId } = {}, -) { +async function clientFor(name, { keypair, contractId } = {}) { if (!contracts[name]) { throw new Error( `Contract ${name} not found. ` + @@ -108,34 +96,22 @@ async function clientFor( ); } - keypair = await keypair; // eslint-disable-line no-param-reassign - const wallet = contract.basicNodeSigner(keypair, networkPassphrase); + const internalKeypair = keypair ?? (await generateFundedKeypair()); + const wallet = contract.basicNodeSigner(internalKeypair, networkPassphrase); let wasmHash = contracts[name].hash; if (!wasmHash) { - wasmHash = spawnSync( - "./target/bin/soroban", - ["contract", "install", "--wasm", contracts[name].path], - { shell: true, encoding: "utf8" }, - ).stdout.trim(); + wasmHash = run( + `${stellar} contract install --wasm ${contracts[name].path}`, + ).stdout; } // TODO: do this with js-stellar-sdk, instead of shelling out to the CLI contractId = contractId ?? - spawnSync( - "./target/bin/soroban", - [ - // eslint-disable-line no-param-reassign - "contract", - "deploy", - "--source", - keypair.secret(), - "--wasm-hash", - wasmHash, - ], - { shell: true, encoding: "utf8" }, - ).stdout.trim(); + run( + `${stellar} contract deploy --source ${internalKeypair.secret()} --wasm-hash ${wasmHash}`, + ).stdout; const client = await contract.Client.fromWasmHash( wasmHash, @@ -144,13 +120,13 @@ async function clientFor( contractId, rpcUrl, allowHttp: true, - publicKey: keypair.publicKey(), + publicKey: internalKeypair.publicKey(), ...wallet, }, "hex", ); return { - keypair, + keypair: internalKeypair, client, contractId, }; diff --git a/test/e2e/wasms/README.md b/test/e2e/wasms/README.md index d44181b20..fa318a0b0 100644 --- a/test/e2e/wasms/README.md +++ b/test/e2e/wasms/README.md @@ -1,16 +1,13 @@ -Copy-pasted `.wasm` files (for now!) -==================================== +Wasm files for testing +====================== -These are built versions of contracts with source code that currently live in -the `test-wasms` directory within soroban-tools. Even there, they are mostly -copy-pasted contracts from `soroban-examples`. +These are copies of Wasm files from testnet or other live networks, used for testing -It would probably be best to include `soroban-examples` as a git submodule, -then directly rely on the contracts we want to test against. No more -copy-pasting! But until we can all agree on such an approach, maybe this is -good enough? +The two files here were created with: -Soon the CLI will have a `soroban init` command that allows setting up a new -Cargo workspace and may allow specifying which example contracts to put in it. -If the git submodule is controversial, then we could use that new `init` -command instead. +``` +stellar contract fetch --id CA4SAAVG73IW6JQK4F45NAR7IAT7DI5MQPRSYSQWEWX4RKUNLL77MNOF --network testnet --out-file test/e2e/wasms/stellar_asset_wrapper.wasm +stellar contract fetch --id CAIGE5STPFOY7V24NNM7KNFNELMXSHZP7ZZV3GZRADRDCUTJ7VCWTQYL --network testnet --out-file test/e2e/wasms/stellar_asset_admin.wasm +``` + +And created based on the bug report in github.com/stellar/stellar-sdk-js/issues/1030 diff --git a/test/e2e/wasms/stellar_asset_admin.wasm b/test/e2e/wasms/stellar_asset_admin.wasm new file mode 100644 index 0000000000000000000000000000000000000000..b0216b5f0aed9d65e5fb97488e0d90e8f595eba6 GIT binary patch literal 723 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 diff --git a/test/e2e/wasms/test_hello_world.wasm b/test/e2e/wasms/test_hello_world.wasm deleted file mode 100755 index 79e09d3dc0d31eb48ee77f83f2216cca4b202789..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7323 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 EKehpf2><{9 diff --git a/test/e2e/wasms/test_token.wasm b/test/e2e/wasms/test_token.wasm deleted file mode 100755 index 6d2f77fac2e3890e346aaeec85f79014b4d4cf12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7494 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 I#`?kNKVX?9Qvd(}