diff --git a/.env b/.env index 996bb6e6b..c9bb883c4 100644 --- a/.env +++ b/.env @@ -15,35 +15,37 @@ ASYNC_STD_THREAD_COUNT=1 PROVER_RAYON_NUM_THREADS=2 # Internal port inside container -ESPRESSO_CDN_SERVER_PORT=40000 +ESPRESSO_CDN_SERVER_PORT=30000 ESPRESSO_CDN_SERVER_METRICS_PORT=9090 -ESPRESSO_ORCHESTRATOR_PORT=40001 +ESPRESSO_ORCHESTRATOR_PORT=30001 ESPRESSO_ORCHESTRATOR_NUM_NODES=5 ESPRESSO_ORCHESTRATOR_START_DELAY=5s -ESPRESSO_ORCHESTRATOR_NEXT_VIEW_TIMEOUT=30s +ESPRESSO_ORCHESTRATOR_NEXT_VIEW_TIMEOUT=12s ESPRESSO_ORCHESTRATOR_BUILDER_TIMEOUT=2s ESPRESSO_SEQUENCER_CDN_ENDPOINT=marshal-0:${ESPRESSO_CDN_SERVER_PORT} ESPRESSO_SEQUENCER_ORCHESTRATOR_URL=http://orchestrator:${ESPRESSO_ORCHESTRATOR_PORT} -ESPRESSO_SEQUENCER_API_PORT=44000 -ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT=42000 -ESPRESSO_SEQUENCER1_API_PORT=44001 -ESPRESSO_SEQUENCER2_API_PORT=44002 -ESPRESSO_SEQUENCER3_API_PORT=44003 -ESPRESSO_SEQUENCER4_API_PORT=44004 -ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE=10kb -ESPRESSO_SEQUENCER_BASE_FEE=1 +ESPRESSO_SEQUENCER_API_PORT=24000 +ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT=22000 +ESPRESSO_SEQUENCER1_API_PORT=24001 +ESPRESSO_SEQUENCER2_API_PORT=24002 +ESPRESSO_SEQUENCER3_API_PORT=24003 +ESPRESSO_SEQUENCER4_API_PORT=24004 ESPRESSO_SEQUENCER_URL=http://sequencer0:${ESPRESSO_SEQUENCER_API_PORT} +ESPRESSO_SEQUENCER_MAX_CONNECTIONS=25 ESPRESSO_SEQUENCER_STORAGE_PATH=/store/sequencer +ESPRESSO_SEQUENCER_GENESIS_FILE=/genesis/demo.toml ESPRESSO_SEQUENCER_L1_PORT=8545 ESPRESSO_SEQUENCER_L1_WS_PORT=8546 ESPRESSO_SEQUENCER_L1_PROVIDER=http://demo-l1-network:${ESPRESSO_SEQUENCER_L1_PORT} +# Only allow 1 block to be processed for events at a time, simulating a very bad L1 provider. +ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE=1 ESPRESSO_SEQUENCER_ETH_MNEMONIC="test test test test test test test test test test test junk" -# The first account is the permission less builder, the last are sequencer0 to 4 -ESPRESSO_SEQUENCER_PREFUNDED_BUILDER_ACCOUNTS=0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f -ESPRESSO_COMMITMENT_TASK_PORT=60000 -ESPRESSO_SEQUENCER_DB_PORT=5432 -ESPRESSO_STATE_RELAY_SERVER_PORT=40004 +ESPRESSO_COMMITMENT_TASK_PORT=30010 +ESPRESSO_SEQUENCER0_DB_PORT=5432 +ESPRESSO_SEQUENCER1_DB_PORT=5433 +ESPRESSO_STATE_RELAY_SERVER_PORT=30011 ESPRESSO_STATE_RELAY_SERVER_URL=http://state-relay-server:${ESPRESSO_STATE_RELAY_SERVER_PORT} +ESPRESSO_BLOCK_EXPLORER_PORT=3000 # Ethereum accounts (note 11-15 are used by the sequencer nodes) ESPRESSO_SEQUENCER_HOTSHOT_ACCOUNT_INDEX=5 @@ -52,8 +54,8 @@ ESPRESSO_BUILDER_ETH_ACCOUNT_INDEX=8 ESPRESSO_DEPLOYER_ACCOUNT_INDEX=9 # Contracts -ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS=0x700b6a60ce7eaaea56f065753d8dcb9653dbad35 -ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS=0xe1aa25618fa0c7a1cfdab5d6b456af611873b629 +ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS=0xb19b36b1456e65e3a6d514d3f715f204bd59f431 +ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS=0x0c8e79f3534b00d9a3d4a856b665bf4ebc22f2ba ESPRESSO_SEQUENCER_LIGHTCLIENT_ADDRESS=$ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS # Example sequencer demo private keys @@ -76,6 +78,9 @@ ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_2=7002 ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_3=7003 ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_4=7004 +# The demo uses the mock stake table contract, only capacity 10 is supported. +ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY=10 + # Foundry # The mnemonic used by foundry to deploy contracts. MNEMONIC="test test test test test test test test test test test junk" @@ -86,31 +91,34 @@ ETHERSCAN_API_KEY="placeholder" # Temporary flags for state relay server, should remove after integrating with stake table # Related issue: [https://github.com/EspressoSystems/espresso-sequencer/issues/1022] -ESPRESSO_STATE_SIGNATURE_WEIGHT_THRESHOLD=3 +ESPRESSO_STATE_SIGNATURE_TOTAL_STAKE=5 # Prover service -ESPRESSO_PROVER_SERVICE_PORT=40050 -ESPRESSO_STATE_PROVER_UPDATE_INTERVAL=10m +ESPRESSO_PROVER_SERVICE_PORT=30050 +ESPRESSO_STATE_PROVER_UPDATE_INTERVAL=20s # Builder service ESPRESSO_BUILDER_L1_PROVIDER=${ESPRESSO_SEQUENCER_L1_PROVIDER} ESPRESSO_BUILDER_ETH_MNEMONIC=${ESPRESSO_SEQUENCER_ETH_MNEMONIC} -ESPRESSO_BUILDER_SERVER_PORT=41003 -ESPRESSO_BUILDER_CHANNEL_CAPACITY=1024 +ESPRESSO_BUILDER_SERVER_PORT=31003 +ESPRESSO_BUILDER_TX_CHANNEL_CAPACITY=4096 +ESPRESSO_BUILDER_EVENT_CHANNEL_CAPACITY=128 +ESPRESSO_BUILDER_INIT_NODE_COUNT=$ESPRESSO_ORCHESTRATOR_NUM_NODES ESPRESSO_BUILDER_BOOTSTRAPPED_VIEW=0 -ESPRESSO_BUILDER_WEBSERVER_RESPONSE_TIMEOUT_DURATION=1s -ESPRESSO_BUILDER_BUFFER_VIEW_NUM_COUNT=15 +ESPRESSO_BUILDER_WEBSERVER_RESPONSE_TIMEOUT_DURATION=1500ms +ESPRESSO_BUILDER_BUFFER_VIEW_NUM_COUNT=50 +ESPRESSO_BUILDER_GENESIS_FILE=$ESPRESSO_SEQUENCER_GENESIS_FILE # Load generator ESPRESSO_SUBMIT_TRANSACTIONS_DELAY=2s -ESPRESSO_SUBMIT_TRANSACTIONS_PUBLIC_PORT=44010 -ESPRESSO_SUBMIT_TRANSACTIONS_PRIVATE_PORT=44020 +ESPRESSO_SUBMIT_TRANSACTIONS_PUBLIC_PORT=24010 +ESPRESSO_SUBMIT_TRANSACTIONS_PRIVATE_PORT=24020 -# Query service stress test -ESPRESSO_NASTY_CLIENT_PORT=44011 +# Query service fetch requests rate limit +ESPRESSO_SEQUENCER_FETCH_RATE_LIMIT=25 # Query service stress test -ESPRESSO_NASTY_CLIENT_PORT=44011 +ESPRESSO_NASTY_CLIENT_PORT=24011 # Openzeppelin Defender Deployment Profile DEFENDER_KEY= @@ -120,3 +128,12 @@ LIGHT_CLIENT_SALT=12 FEE_CONTRACT_UPGRADE_NAME="FeeContract.sol" LIGHT_CLIENT_UPGRADE_NAME="LightClientV2.sol" FOUNDRY_OUT=contracts/out + +# The Ethereum address of the safe multisig wallet used to deploy and operate the contracts. +SAFE_MULTISIG_ADDRESS= +# The Ethereum private key of the wallet used for the proposing multisig transactions. +SAFE_ORCHESTRATOR_PRIVATE_KEY= + +# Light Client +LIGHT_CLIENT_CONTRACT_ADDRESS= +APPROVED_PROVER_ADDRESS= diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8191c0344..49b9f68a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,6 +52,11 @@ jobs: run: | cargo build --locked --release --workspace + - name: Build Espresso Dev Node + # Espresso Dev Node currently requires testing feature, so it is built separately. + run: | + cargo build --locked --release --features testing --bin espresso-dev-node + - name: Upload artifacts uses: actions/upload-artifact@v3 with: @@ -72,7 +77,9 @@ jobs: target/release/keygen target/release/permissionless-builder target/release/nasty-client + target/release/espresso-dev-node target/release/pub-key + target/release/espresso-bridge build-arm: runs-on: buildjet-4vcpu-ubuntu-2204-arm @@ -96,6 +103,11 @@ jobs: run: | cargo build --locked --release --workspace + - name: Build Espresso Dev Node + # Espresso Dev Node currently requires testing feature, so it is built separately. + run: | + cargo build --locked --release --features testing --bin espresso-dev-node + - name: Upload artifacts uses: actions/upload-artifact@v3 with: @@ -116,7 +128,9 @@ jobs: target/release/keygen target/release/permissionless-builder target/release/nasty-client + target/release/espresso-dev-node target/release/pub-key + target/release/espresso-bridge build-dockers: runs-on: ubuntu-latest @@ -134,6 +148,8 @@ jobs: deploy-tag: ${{ steps.deploy.outputs.tags }} builder-tag: ${{ steps.builder.outputs.tags }} nasty-client-tag: ${{ steps.nasty-client.outputs.tags }} + espresso-dev-node-tag: ${{ steps.espresso-dev-node.outputs.tags }} + bridge-tag: ${{ steps.bridge.outputs.tags }} steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -235,8 +251,20 @@ jobs: with: images: ghcr.io/espressosystems/espresso-sequencer/nasty-client + - name: Generate espresso-dev-node metadata + uses: docker/metadata-action@v5 + id: espresso-dev-node + with: + images: ghcr.io/espressosystems/espresso-sequencer/espresso-dev-node + + - name: Generate bridge metadata + uses: docker/metadata-action@v5 + id: bridge + with: + images: ghcr.io/espressosystems/espresso-sequencer/bridge + - name: Build and push sequencer docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/sequencer.Dockerfile @@ -246,7 +274,7 @@ jobs: labels: ${{ steps.sequencer.outputs.labels }} - name: Build and push cdn-broker docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/cdn-broker.Dockerfile @@ -256,7 +284,7 @@ jobs: labels: ${{ steps.cdn-broker.outputs.labels }} - name: Build and push cdn-marshal docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/cdn-marshal.Dockerfile @@ -266,7 +294,7 @@ jobs: labels: ${{ steps.cdn-marshal.outputs.labels }} - name: Build and push cdn-whitelist docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/cdn-whitelist.Dockerfile @@ -276,7 +304,7 @@ jobs: labels: ${{ steps.cdn-whitelist.outputs.labels }} - name: Build and push state-relay-server docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/state-relay-server.Dockerfile @@ -286,7 +314,7 @@ jobs: labels: ${{ steps.state-relay-server.outputs.labels }} - name: Build and push prover-service docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/prover-service.Dockerfile @@ -296,7 +324,7 @@ jobs: labels: ${{ steps.prover-service.outputs.labels }} - name: Build and push orchestrator docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/orchestrator.Dockerfile @@ -306,7 +334,7 @@ jobs: labels: ${{ steps.orchestrator.outputs.labels }} - name: Build and push commitment-task docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/commitment-task.Dockerfile @@ -316,7 +344,7 @@ jobs: labels: ${{ steps.commitment-task.outputs.labels }} - name: Build and push submit-transactions docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/submit-transactions.Dockerfile @@ -326,7 +354,7 @@ jobs: labels: ${{ steps.submit-transactions.outputs.labels }} - name: Build and push deploy docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/deploy.Dockerfile @@ -336,7 +364,7 @@ jobs: labels: ${{ steps.deploy.outputs.labels }} - name: Build and push builder docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/permissionless-builder.Dockerfile @@ -346,7 +374,7 @@ jobs: labels: ${{ steps.builder.outputs.labels }} - name: Build and push nasty-client docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/nasty-client.Dockerfile @@ -355,6 +383,25 @@ jobs: tags: ${{ steps.nasty-client.outputs.tags }} labels: ${{ steps.nasty-client.outputs.labels }} + - name: Build and push espresso-dev-node docker + uses: docker/build-push-action@v6 + with: + context: ./ + file: ./docker/espresso-dev-node.Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.espresso-dev-node.outputs.tags }} + labels: ${{ steps.espresso-dev-node.outputs.labels }} + - name: Build and push bridge docker + uses: docker/build-push-action@v6 + with: + context: ./ + file: ./docker/espresso-bridge.Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.bridge.outputs.tags }} + labels: ${{ steps.bridge.outputs.labels }} + test-demo: if: ${{ github.event_name != 'pull_request' }} runs-on: ubuntu-latest @@ -382,6 +429,7 @@ jobs: docker pull ${{ needs.build-dockers.outputs.deploy-tag }} docker pull ${{ needs.build-dockers.outputs.builder-tag }} docker pull ${{ needs.build-dockers.outputs.nasty-client-tag }} + docker pull ${{ needs.build-dockers.outputs.bridge-tag }} - name: Tag new docker images run: | docker tag ${{ needs.build-dockers.outputs.sequencer-tag }} ghcr.io/espressosystems/espresso-sequencer/sequencer:main @@ -396,8 +444,9 @@ jobs: docker tag ${{ needs.build-dockers.outputs.deploy-tag }} ghcr.io/espressosystems/espresso-sequencer/deploy:main docker tag ${{ needs.build-dockers.outputs.builder-tag }} ghcr.io/espressosystems/espresso-sequencer/builder:main docker tag ${{ needs.build-dockers.outputs.nasty-client-tag }} ghcr.io/espressosystems/espresso-sequencer/nasty-client:main + docker tag ${{ needs.build-dockers.outputs.bridge-tag }} ghcr.io/espressosystems/espresso-sequencer/bridge:main - name: Test docker demo run: | just demo & - timeout 600 scripts/smoke-test-demo + timeout -v 600 scripts/smoke-test-demo | sed -e 's/^/smoke-test: /;' diff --git a/.github/workflows/build_static.yml b/.github/workflows/build_static.yml index e0727ca09..0d533fd38 100644 --- a/.github/workflows/build_static.yml +++ b/.github/workflows/build_static.yml @@ -43,10 +43,10 @@ jobs: uses: actions/checkout@v4 - name: Install Nix - uses: cachix/install-nix-action@v26 + uses: cachix/install-nix-action@V27 - name: Enable Cachix - uses: cachix/cachix-action@v14 + uses: cachix/cachix-action@v15 # If PR is from a non-collaborator (e. g. dependabot) the secrets are missing and the login to cachix fails. continue-on-error: true with: @@ -89,6 +89,8 @@ jobs: ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/deploy ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/keygen ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/pub-key + ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/espresso-bridge + ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/espresso-dev-node static-dockers: runs-on: ubuntu-latest @@ -192,8 +194,22 @@ jobs: images: ghcr.io/espressosystems/espresso-sequencer/deploy flavor: suffix=musl + - name: Generate espresso-dev-node metadata + uses: docker/metadata-action@v5 + id: espresso-dev-node + with: + images: ghcr.io/espressosystems/espresso-sequencer/espresso-dev-node + flavor: suffix=musl + + - name: Generate bridge metadata + uses: docker/metadata-action@v5 + id: bridge + with: + images: ghcr.io/espressosystems/espresso-sequencer/bridge + flavor: suffix=musl + - name: Build and push sequencer docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/sequencer.Dockerfile @@ -203,7 +219,7 @@ jobs: labels: ${{ steps.sequencer.outputs.labels }} - name: Build and push cdn-broker docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/cdn-broker.Dockerfile @@ -213,7 +229,7 @@ jobs: labels: ${{ steps.cdn-broker.outputs.labels }} - name: Build and push cdn-marshal docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/cdn-marshal.Dockerfile @@ -223,7 +239,7 @@ jobs: labels: ${{ steps.cdn-marshal.outputs.labels }} - name: Build and push cdn-whitelist docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/cdn-whitelist.Dockerfile @@ -233,7 +249,7 @@ jobs: labels: ${{ steps.cdn-whitelist.outputs.labels }} - name: Build and push state-relay-server docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/state-relay-server.Dockerfile @@ -243,7 +259,7 @@ jobs: labels: ${{ steps.state-relay-server.outputs.labels }} - name: Build and push prover-service docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/prover-service.Dockerfile @@ -253,7 +269,7 @@ jobs: labels: ${{ steps.prover-service.outputs.labels }} - name: Build and push orchestrator docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/orchestrator.Dockerfile @@ -263,7 +279,7 @@ jobs: labels: ${{ steps.orchestrator.outputs.labels }} - name: Build and push commitment-task docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/commitment-task.Dockerfile @@ -273,7 +289,7 @@ jobs: labels: ${{ steps.commitment-task.outputs.labels }} - name: Build and push submit-transactions docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/submit-transactions.Dockerfile @@ -283,7 +299,7 @@ jobs: labels: ${{ steps.submit-transactions.outputs.labels }} - name: Build and push deploy docker - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./docker/deploy.Dockerfile @@ -291,3 +307,23 @@ jobs: push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.deploy.outputs.tags }} labels: ${{ steps.deploy.outputs.labels }} + + - name: Build and push dev node docker + uses: docker/build-push-action@v6 + with: + context: ./ + file: ./docker/espresso-dev-node.Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.espresso-dev-node.outputs.tags }} + labels: ${{ steps.espresso-dev-node.outputs.labels }} + + - name: Build and push bridge docker + uses: docker/build-push-action@v6 + with: + context: ./ + file: ./docker/espresso-bridge.Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.bridge.outputs.tags }} + labels: ${{ steps.bridge.outputs.labels }} diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index 0847511f7..0d5fad542 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -25,10 +25,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Nix - uses: cachix/install-nix-action@v26 + uses: cachix/install-nix-action@V27 - name: Enable Cachix - uses: cachix/cachix-action@v14 + uses: cachix/cachix-action@v15 # If PR is from a non-collaborator (e. g. dependabot) the secrets are missing and the login to cachix fails. continue-on-error: true with: diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 000000000..2f4449213 --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,49 @@ +name: Documentation +on: + push: + branches: + - "main" + - "release-*" + pull_request: + schedule: + - cron: "0 0 * * 1" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ (github.ref == 'refs/heads/main' && github.run_number) || github.ref }} + cancel-in-progress: true + +env: + RUSTFLAGS: --cfg async_executor_impl="async-std" --cfg async_channel_impl="async-std" + RUSTDOCFLAGS: --cfg async_executor_impl="async-std" --cfg async_channel_impl="async-std" + +jobs: + doc: + runs-on: ubuntu-24.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Install Just + run: sudo apt-get install -y just + + - uses: Swatinem/rust-cache@v2 + name: Enable Rust Caching + + - name: Build Docs + run: | + just doc + + - name: Create documentation + if: ${{ github.ref == 'refs/heads/main' }} + run: | + cp -R target/doc public + echo '' > public/index.html + + - name: Deploy + uses: peaceiris/actions-gh-pages@v4 + if: ${{ github.ref == 'refs/heads/main' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./public + cname: sequencer.docs.espressosys.com diff --git a/.github/workflows/test-demo-native.yml b/.github/workflows/test-demo-native.yml index 90666c366..ab85b1a75 100644 --- a/.github/workflows/test-demo-native.yml +++ b/.github/workflows/test-demo-native.yml @@ -29,7 +29,7 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 - name: Install Nix - uses: cachix/install-nix-action@v26 + uses: cachix/install-nix-action@V27 - name: Install process-compose run: | @@ -50,4 +50,4 @@ jobs: run: | export PATH="$PWD/target/release:$PATH" scripts/demo-native --tui=false & - timeout -v 600 scripts/smoke-test-demo + timeout -v 600 scripts/smoke-test-demo | sed -e 's/^/smoke-test: /;' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 37c4c45fd..b50fdfece 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,4 +55,4 @@ jobs: cargo build --locked --bin diff-test --release cargo test --locked --release --workspace --all-features --no-run cargo test --locked --release --workspace --all-features --verbose -- --test-threads 1 --nocapture - timeout-minutes: 30 + timeout-minutes: 40 diff --git a/.github/workflows/update_nix.yml b/.github/workflows/update_nix.yml index a2783722c..f7dfd33f6 100644 --- a/.github/workflows/update_nix.yml +++ b/.github/workflows/update_nix.yml @@ -17,9 +17,9 @@ jobs: uses: actions/checkout@v4 - name: Install Nix - uses: cachix/install-nix-action@v26 + uses: cachix/install-nix-action@V27 - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v15 with: name: espresso-systems-private authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" diff --git a/.gitignore b/.gitignore index fac0a2025..4503c6c73 100644 --- a/.gitignore +++ b/.gitignore @@ -14,9 +14,7 @@ target/ # Jetbrains editor .idea - -# vscode stuff -.vscode/settings.json +.vscode/ltex.dictionary* node_modules/ @@ -26,3 +24,20 @@ node_modules/ cache **/*.bin + + +yarn.lock + +# wake files +.wake +.env +pytypes +__pycache__/ +*.py[cod] +.hypothesis/ +wake-coverage.cov +!data/*.bin + +# generated by failing serialization tests +data/*-actual.json +data/*-actual.bin diff --git a/.gitmodules b/.gitmodules index 1dbb51036..d4556787d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,7 +19,7 @@ [submodule "contracts/lib/openzeppelin-foundry-upgrades"] path = contracts/lib/openzeppelin-foundry-upgrades url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades - branch = 0.1.0 + branch = 0.2.3 [submodule "contracts/lib/openzeppelin-contracts-upgradeable"] path = contracts/lib/openzeppelin-contracts-upgradeable url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/.typos.toml b/.typos.toml index 71189ef29..53cdcf235 100644 --- a/.typos.toml +++ b/.typos.toml @@ -1,7 +1,7 @@ [files] extend-exclude = [ + "*.json", "doc/*.svg", - "geth-config/*.json", "contracts/lib", "contract-bindings", ] diff --git a/.vscode/ltex.dictionary.en-US.txt b/.vscode/ltex.dictionary.en-US.txt new file mode 100644 index 000000000..12589cd69 --- /dev/null +++ b/.vscode/ltex.dictionary.en-US.txt @@ -0,0 +1,4 @@ +Multisig +OpenZeppelin +multisig +Metamask diff --git a/.vscode/settings.json.example b/.vscode/settings.json similarity index 100% rename from .vscode/settings.json.example rename to .vscode/settings.json diff --git a/Cargo.lock b/Cargo.lock index 8efc37181..64ea70564 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,7 +122,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "version_check", ] @@ -134,7 +134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -172,47 +172,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -220,9 +221,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" @@ -401,17 +402,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "ark-pallas" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760ecac12a00211188c9101b63bd284b80da5abcc5d97d9d2b3803bca1f63a52" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - [[package]] name = "ark-poly" version = "0.4.2" @@ -547,9 +537,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -563,25 +553,25 @@ dependencies = [ [[package]] name = "asn1-rs-derive" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure 0.12.6", + "syn 2.0.67", + "synstructure 0.13.1", ] [[package]] name = "asn1-rs-impl" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.67", ] [[package]] @@ -602,12 +592,12 @@ dependencies = [ [[package]] name = "async-broadcast" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" dependencies = [ - "event-listener 5.3.0", - "event-listener-strategy 0.5.2", + "event-listener 5.3.1", + "event-listener-strategy", "futures-core", "pin-project-lite 0.2.14", ] @@ -625,28 +615,27 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 5.3.0", - "event-listener-strategy 0.5.2", + "event-listener-strategy", "futures-core", "pin-project-lite 0.2.14", ] [[package]] name = "async-compatibility-layer" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7d24a8f9d6fec5b9313ceebbd82133ae87cf94944c362e8169d7ec735c88a23" +checksum = "2c928880329566b45c159fca61fbc2a1d301a7e5fd2a0e94c17476bb1a3ea526" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "async-std", "async-trait", "color-eyre", - "console-subscriber", + "console-subscriber 0.2.0", "flume 0.11.0", "futures", "tokio", @@ -662,15 +651,15 @@ version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c2886ab563af5038f79ec016dd7b87947ed138b794e8dd64992962c9cca0411" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "futures-io", ] [[package]] name = "async-executor" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" dependencies = [ "async-task", "concurrent-queue", @@ -697,10 +686,10 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.2.1", + "async-channel 2.3.1", "async-executor", - "async-io 2.3.2", - "async-lock 3.3.0", + "async-io 2.3.3", + "async-lock 3.4.0", "blocking", "futures-lite 2.3.0", "once_cell", @@ -746,17 +735,17 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.3.0", "parking", - "polling 3.7.0", + "polling 3.7.2", "rustix 0.38.34", "slab", "tracing", @@ -774,12 +763,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener 5.3.1", + "event-listener-strategy", "pin-project-lite 0.2.14", ] @@ -831,12 +820,12 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" dependencies = [ - "async-io 2.3.2", - "async-lock 3.3.0", + "async-io 2.3.3", + "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", @@ -923,7 +912,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -953,7 +942,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -1056,14 +1045,14 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "automod" @@ -1073,7 +1062,7 @@ checksum = "edf3ee19dbc0a46d740f6f0926bde8c50f02bdbc7b536842da28f6ac56513a8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -1089,7 +1078,7 @@ dependencies = [ "futures-util", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.29", "itoa", "matchit", "memchr", @@ -1098,7 +1087,7 @@ dependencies = [ "pin-project-lite 0.2.14", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 0.1.2", "tower", "tower-layer", "tower-service", @@ -1162,9 +1151,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64-bytes" @@ -1172,7 +1161,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ce54e4e485fa0eed9c3aa5348162be09168f75bb5be7bc6587bcf2a65ee1386" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "serde", ] @@ -1291,12 +1280,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel 2.2.1", - "async-lock 3.3.0", + "async-channel 2.3.1", "async-task", "futures-io", "futures-lite 2.3.0", @@ -1305,9 +1293,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" dependencies = [ "cc", "glob", @@ -1350,19 +1338,22 @@ dependencies = [ "hotshot-stake-table", "hotshot-state-prover", "hotshot-types", - "jf-primitives 0.4.4", + "jf-merkle-tree", + "jf-signature", + "libp2p", "portpicker", "rand 0.8.5", "sequencer", "serde", - "snafu 0.8.2", + "snafu 0.8.3", "surf", "surf-disco", - "tagged-base64 0.4.0", + "tagged-base64", "tide-disco", "tracing", "url", "vbs", + "vec1", ] [[package]] @@ -1449,22 +1440,31 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] [[package]] name = "capnp" -version = "0.19.3" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b11832e6fb7a695c4a63cc42bd97bd2cda7165cd850caf5aff9a3d0e617720ed" +checksum = "de71387912cac7dd3cb7c219e09628411620a18061bba58c71453c26ae7bf66a" dependencies = [ "embedded-io", ] +[[package]] +name = "capnpc" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ba30e0f08582d53c2f3710cf4bb65ff562614b1ba86906d7391adffe189ec" +dependencies = [ + "capnp", +] + [[package]] name = "cargo-platform" version = "0.1.8" @@ -1482,7 +1482,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -1499,9 +1499,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.95" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", @@ -1511,39 +1511,19 @@ dependencies = [ [[package]] name = "cdn-broker" version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4#31a2f7f97ba32f8b2894fae4e7d3e5c484eb3675" -dependencies = [ - "async-std", - "cdn-proto 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4)", - "clap", - "dashmap", - "derivative", - "jf-primitives 0.4.0-pre.0", - "lazy_static", - "local-ip-address", - "parking_lot", - "prometheus", - "rand 0.8.5", - "rkyv", - "tokio", - "tracing", - "tracing-subscriber 0.3.18", -] - -[[package]] -name = "cdn-broker" -version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.6#9e494917ff46732ac669ce07c5dcb8a989bbc03a" +source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.12#7bf490b34fb5b996d7e98d3d4be6d88028a076ac" dependencies = [ "async-std", - "cdn-proto 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.6)", + "cdn-proto", "clap", + "console-subscriber 0.3.0", "dashmap", "derivative", - "jf-primitives 0.4.0-pre.0", + "jf-signature", "lazy_static", "local-ip-address", "parking_lot", + "portpicker", "prometheus", "rand 0.8.5", "rkyv", @@ -1555,12 +1535,12 @@ dependencies = [ [[package]] name = "cdn-client" version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4#31a2f7f97ba32f8b2894fae4e7d3e5c484eb3675" +source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.12#7bf490b34fb5b996d7e98d3d4be6d88028a076ac" dependencies = [ "async-std", - "cdn-proto 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4)", + "cdn-proto", "clap", - "jf-primitives 0.4.0-pre.0", + "jf-signature", "rand 0.8.5", "tokio", "tracing", @@ -1570,26 +1550,12 @@ dependencies = [ [[package]] name = "cdn-marshal" version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4#31a2f7f97ba32f8b2894fae4e7d3e5c484eb3675" -dependencies = [ - "async-std", - "cdn-proto 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4)", - "clap", - "jf-primitives 0.4.0-pre.0", - "tokio", - "tracing", - "tracing-subscriber 0.3.18", -] - -[[package]] -name = "cdn-marshal" -version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.6#9e494917ff46732ac669ce07c5dcb8a989bbc03a" +source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.12#7bf490b34fb5b996d7e98d3d4be6d88028a076ac" dependencies = [ "async-std", - "cdn-proto 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.6)", + "cdn-proto", "clap", - "jf-primitives 0.4.0-pre.0", + "jf-signature", "tokio", "tracing", "tracing-subscriber 0.3.18", @@ -1598,62 +1564,32 @@ dependencies = [ [[package]] name = "cdn-proto" version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4#31a2f7f97ba32f8b2894fae4e7d3e5c484eb3675" -dependencies = [ - "anyhow", - "ark-serialize", - "async-trait", - "capnp", - "derivative", - "jf-primitives 0.4.0-pre.0", - "kanal", - "lazy_static", - "mnemonic", - "mockall", - "parking_lot", - "pem 3.0.4", - "prometheus", - "quinn", - "rand 0.8.5", - "rcgen 0.12.1", - "redis", - "rkyv", - "rustls 0.21.12", - "sqlx", - "thiserror", - "tokio", - "tracing", - "url", - "warp", -] - -[[package]] -name = "cdn-proto" -version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.6#9e494917ff46732ac669ce07c5dcb8a989bbc03a" +source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.12#7bf490b34fb5b996d7e98d3d4be6d88028a076ac" dependencies = [ "anyhow", "ark-serialize", "async-trait", "capnp", + "capnpc", "derivative", - "jf-primitives 0.4.0-pre.0", + "jf-signature", "kanal", "lazy_static", "mnemonic", - "mockall", - "parking_lot", + "num_enum", "pem 3.0.4", "prometheus", "quinn", "rand 0.8.5", - "rcgen 0.12.1", + "rcgen 0.13.1", "redis", "rkyv", - "rustls 0.21.12", + "rustls 0.23.10", + "rustls-pki-types", "sqlx", "thiserror", "tokio", + "tokio-rustls 0.26.0", "tracing", "url", "warp", @@ -1726,9 +1662,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -1736,33 +1672,33 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "cld" @@ -1851,9 +1787,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "combine" @@ -1884,7 +1820,7 @@ dependencies = [ "hex", "serde", "sha3", - "tagged-base64 0.4.0", + "tagged-base64", ] [[package]] @@ -1925,7 +1861,20 @@ dependencies = [ "futures-core", "prost", "prost-types", - "tonic", + "tonic 0.10.2", + "tracing-core", +] + +[[package]] +name = "console-api" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd" +dependencies = [ + "futures-core", + "prost", + "prost-types", + "tonic 0.11.0", "tracing-core", ] @@ -1935,7 +1884,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7481d4c57092cd1c19dd541b92bdce883de840df30aa5d03fd48a3935c01842e" dependencies = [ - "console-api", + "console-api 0.6.0", "crossbeam-channel", "crossbeam-utils", "futures-task", @@ -1947,7 +1896,32 @@ dependencies = [ "thread_local", "tokio", "tokio-stream", - "tonic", + "tonic 0.10.2", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.18", +] + +[[package]] +name = "console-subscriber" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c4cc54bae66f7d9188996404abdf7fdfa23034ef8e43478c8810828abad758" +dependencies = [ + "console-api 0.7.0", + "crossbeam-channel", + "crossbeam-utils", + "futures-task", + "hdrhistogram", + "humantime", + "prost", + "prost-types", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic 0.11.0", "tracing", "tracing-core", "tracing-subscriber 0.3.18", @@ -1955,9 +1929,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -1987,7 +1961,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "tiny-keccak", ] @@ -2101,9 +2075,9 @@ dependencies = [ [[package]] name = "crc-any" -version = "2.4.4" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01a5e1f881f6fb6099a7bdf949e946719fd4f1fefa56264890574febf0eb6d0" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" [[package]] name = "crc-catalog" @@ -2113,18 +2087,18 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -2159,9 +2133,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -2212,18 +2186,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "crypto_kx" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704722d1d929489c8528bb1882805700f1ba20f54325704973e786352320b1ed" -dependencies = [ - "blake2", - "curve25519-dalek", - "rand_core 0.6.4", - "serdect", -] - [[package]] name = "csv" version = "1.3.0" @@ -2307,16 +2269,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", "rustc_version 0.4.0", "subtle", "zeroize", @@ -2330,7 +2291,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -2372,15 +2333,15 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", "synstructure 0.13.1", ] [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -2388,27 +2349,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.60", + "strsim", + "syn 2.0.67", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -2463,9 +2424,9 @@ dependencies = [ [[package]] name = "der-parser" -version = "8.2.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" dependencies = [ "asn1-rs", "displaydoc", @@ -2504,7 +2465,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -2525,7 +2486,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -2535,22 +2496,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 1.0.109", + "syn 2.0.67", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "diff-test-bn254" version = "0.1.0" @@ -2585,10 +2552,11 @@ dependencies = [ "hotshot-state-prover", "hotshot-types", "itertools 0.12.1", + "jf-pcs", "jf-plonk", - "jf-primitives 0.4.4", - "jf-relation 0.4.4", - "jf-utils 0.4.4", + "jf-relation", + "jf-signature", + "jf-utils", "sha3", ] @@ -2672,13 +2640,13 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -2702,12 +2670,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "downcast" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" - [[package]] name = "downcast-rs" version = "1.2.1" @@ -2784,9 +2746,9 @@ checksum = "bbbaaaf38131deb9ca518a274a45bfdb8771f139517b073b16c2d3d32ae5037b" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" dependencies = [ "serde", ] @@ -2818,9 +2780,9 @@ checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" [[package]] name = "ena" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] @@ -2861,7 +2823,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -2872,18 +2834,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" dependencies = [ "serde", + "typeid", ] [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2897,6 +2860,18 @@ dependencies = [ "vbs", ] +[[package]] +name = "escargot" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eb5f6eeda986377996e9ed570cbc20cc16d30440696f82f129c863e4e3e83" +dependencies = [ + "log", + "once_cell", + "serde", + "serde_json", +] + [[package]] name = "espresso-macros" version = "0.1.0" @@ -3064,7 +3039,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.60", + "syn 2.0.67", "toml", "walkdir", ] @@ -3082,7 +3057,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -3108,7 +3083,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.60", + "syn 2.0.67", "tempfile", "thiserror", "tiny-keccak", @@ -3125,7 +3100,7 @@ dependencies = [ "ethers-core", "ethers-solc", "reqwest 0.11.27", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -3234,7 +3209,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "solang-parser", @@ -3266,43 +3241,22 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite 0.2.14", -] - -[[package]] -name = "event-listener" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", "pin-project-lite 0.2.14", ] -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite 0.2.14", -] - [[package]] name = "event-listener-strategy" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.3.0", + "event-listener 5.3.1", "pin-project-lite 0.2.14", ] @@ -3349,15 +3303,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" - -[[package]] -name = "finl_unicode" -version = "1.2.0" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fixed-hash" @@ -3387,6 +3335,16 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fluent-asserter" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cd2a1243f15c8c9d37acc8ab4ba837e50823561cb124af8406a6f676d04341" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "flume" version = "0.9.2" @@ -3440,12 +3398,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - [[package]] name = "fs2" version = "0.4.3" @@ -3479,9 +3431,9 @@ dependencies = [ [[package]] name = "futures-bounded" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e2774cc104e198ef3d3e1ff4ab40f86fa3245d6cb6a3a46174f21463cee173" +checksum = "91f328e7fb845fc832912fb6a34f40cf6d1888c92f974d1893a54e97b5ff542e" dependencies = [ "futures-timer", "futures-util", @@ -3578,7 +3530,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -3591,6 +3543,17 @@ dependencies = [ "rustls 0.21.12", ] +[[package]] +name = "futures-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" +dependencies = [ + "futures-io", + "rustls 0.23.10", + "rustls-pki-types", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -3659,7 +3622,7 @@ dependencies = [ "hotshot-contract-adapter", "hotshot-stake-table", "hotshot-state-prover", - "jf-primitives 0.4.4", + "jf-pcs", ] [[package]] @@ -3687,9 +3650,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -3774,15 +3737,15 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" dependencies = [ + "atomic-waker", "bytes 1.6.0", "fnv", "futures-core", "futures-sink", - "futures-util", "http 1.1.0", "indexmap 2.2.6", "slab", @@ -3895,6 +3858,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -4040,8 +4009,8 @@ dependencies = [ [[package]] name = "hotshot" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "anyhow", "async-broadcast", @@ -4052,9 +4021,10 @@ dependencies = [ "bimap", "bincode", "blake3", - "cdn-broker 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4)", + "cdn-broker", "cdn-client", - "cdn-marshal 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4)", + "cdn-marshal", + "chrono", "committable", "custom_debug 0.5.1", "dashmap", @@ -4066,16 +4036,16 @@ dependencies = [ "hotshot-task", "hotshot-task-impls", "hotshot-types", - "hotshot-web-server", - "jf-primitives 0.4.4", + "jf-signature", "libp2p-identity", "libp2p-networking", "lru 0.12.3", + "num_enum", "portpicker", "rand 0.8.5", "serde", "sha2 0.10.8", - "snafu 0.8.2", + "snafu 0.8.3", "surf-disco", "time 0.3.36", "tokio", @@ -4086,7 +4056,7 @@ dependencies = [ [[package]] name = "hotshot-builder-api" version = "0.1.7" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "async-trait", "clap", @@ -4095,8 +4065,8 @@ dependencies = [ "futures", "hotshot-types", "serde", - "snafu 0.8.2", - "tagged-base64 0.4.0", + "snafu 0.8.3", + "tagged-base64", "tide-disco", "toml", "vbs", @@ -4104,9 +4074,10 @@ dependencies = [ [[package]] name = "hotshot-builder-core" -version = "0.1.14" -source = "git+https://github.com/EspressoSystems/hotshot-builder-core?tag=0.1.14#2e873a3775a9f412bb084afef25e419e8ecd1773" +version = "0.1.26" +source = "git+https://github.com/EspressoSystems/hotshot-builder-core?tag=rc-0.1.29#2f5d1b7bb17bb667cf34df804b95a2816aea6dd2" dependencies = [ + "anyhow", "async-broadcast", "async-compatibility-layer", "async-lock 2.8.0", @@ -4114,6 +4085,7 @@ dependencies = [ "async-trait", "clap", "committable", + "derivative", "futures", "hotshot", "hotshot-builder-api", @@ -4121,10 +4093,11 @@ dependencies = [ "hotshot-types", "serde", "sha2 0.10.8", - "snafu 0.8.2", + "snafu 0.8.3", "surf-disco", - "tagged-base64 0.4.0", + "tagged-base64", "tide-disco", + "tokio", "tracing", "vbs", ] @@ -4145,17 +4118,17 @@ dependencies = [ "diff-test-bn254", "ethers", "hotshot-types", + "jf-pcs", "jf-plonk", - "jf-primitives 0.4.4", - "jf-utils 0.4.4", + "jf-utils", "num-bigint", "num-traits", ] [[package]] name = "hotshot-events-service" -version = "0.1.15" -source = "git+https://github.com/EspressoSystems/hotshot-events-service.git?tag=0.1.16#dc8d019f7f487ad2bbb87cc934498d0abfc4af13" +version = "0.1.27" +source = "git+https://github.com/EspressoSystems/hotshot-events-service.git?tag=rc-0.1.29#0aa74a42b4bae9dc11011684e7a10346590fa068" dependencies = [ "async-broadcast", "async-compatibility-layer", @@ -4169,8 +4142,8 @@ dependencies = [ "futures", "hotshot-types", "serde", - "snafu 0.8.2", - "tagged-base64 0.4.0", + "snafu 0.8.3", + "tagged-base64", "tide-disco", "toml", "tracing", @@ -4179,8 +4152,8 @@ dependencies = [ [[package]] name = "hotshot-example-types" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "anyhow", "async-broadcast", @@ -4201,27 +4174,28 @@ dependencies = [ "serde", "sha2 0.10.8", "sha3", - "snafu 0.8.2", + "snafu 0.8.3", "time 0.3.36", "tokio", "tracing", + "vbs", ] [[package]] name = "hotshot-macros" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "derive_builder", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "hotshot-orchestrator" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "anyhow", "async-compatibility-layer", @@ -4245,24 +4219,24 @@ dependencies = [ "toml", "tracing", "vbs", + "vec1", ] [[package]] name = "hotshot-query-service" -version = "0.1.14" -source = "git+https://github.com/EspressoSystems/hotshot-query-service?tag=0.1.14#4a10a39e34888afd8b56705bf0991a391a7d35c9" +version = "0.1.39" +source = "git+https://github.com/EspressoSystems/hotshot-query-service?tag=rc-0.1.41#bd03a5d909e8705f6038f2066a7633732c653219" dependencies = [ "anyhow", "ark-serialize", "async-compatibility-layer", + "async-lock 3.4.0", "async-std", "async-trait", "atomic_store", "bincode", "bit-vec", "chrono", - "clap", - "cld", "committable", "custom_debug 0.6.1", "derivative", @@ -4276,7 +4250,8 @@ dependencies = [ "hotshot-types", "include_dir", "itertools 0.12.1", - "jf-primitives 0.4.4", + "jf-merkle-tree", + "jf-vid", "native-tls", "portpicker", "postgres-native-tls", @@ -4285,10 +4260,10 @@ dependencies = [ "refinery", "serde", "serde_json", - "snafu 0.8.2", + "snafu 0.8.3", "spin_sleep", "surf-disco", - "tagged-base64 0.4.0", + "tagged-base64", "tempfile", "tide-disco", "time 0.3.36", @@ -4299,12 +4274,13 @@ dependencies = [ "trait-variant", "typenum", "vbs", + "vec1", ] [[package]] name = "hotshot-stake-table" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "ark-bn254", "ark-ed-on-bn254", @@ -4314,10 +4290,12 @@ dependencies = [ "digest 0.10.7", "ethereum-types", "hotshot-types", - "jf-primitives 0.4.4", - "jf-utils 0.4.4", + "jf-crhf", + "jf-rescue", + "jf-signature", + "jf-utils", "serde", - "tagged-base64 0.4.0", + "tagged-base64", ] [[package]] @@ -4349,16 +4327,20 @@ dependencies = [ "hotshot-stake-table", "hotshot-types", "itertools 0.12.1", + "jf-crhf", + "jf-pcs", "jf-plonk", - "jf-primitives 0.4.4", - "jf-relation 0.4.4", - "jf-utils 0.4.4", + "jf-relation", + "jf-rescue", + "jf-signature", + "jf-utils", "rand_chacha 0.3.1", + "reqwest 0.12.5", "sequencer-utils", "serde", - "snafu 0.8.2", + "snafu 0.8.3", "surf-disco", - "tagged-base64 0.3.4", + "tagged-base64", "tide-disco", "time 0.3.36", "toml", @@ -4369,12 +4351,14 @@ dependencies = [ [[package]] name = "hotshot-task" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ + "anyhow", "async-broadcast", "async-compatibility-layer", "async-std", + "async-trait", "futures", "tokio", "tracing", @@ -4382,8 +4366,8 @@ dependencies = [ [[package]] name = "hotshot-task-impls" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "anyhow", "async-broadcast", @@ -4393,6 +4377,7 @@ dependencies = [ "async-trait", "bincode", "bitvec", + "cdn-proto", "chrono", "committable", "either", @@ -4400,13 +4385,14 @@ dependencies = [ "hotshot-builder-api", "hotshot-task", "hotshot-types", - "jf-primitives 0.4.4", + "jf-signature", + "jf-vid", "rand 0.8.5", "serde", "sha2 0.10.8", - "snafu 0.8.2", + "snafu 0.8.3", "surf-disco", - "tagged-base64 0.4.0", + "tagged-base64", "time 0.3.36", "tokio", "tracing", @@ -4415,9 +4401,10 @@ dependencies = [ [[package]] name = "hotshot-testing" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ + "anyhow", "async-broadcast", "async-compatibility-layer", "async-lock 2.8.0", @@ -4437,25 +4424,27 @@ dependencies = [ "hotshot-task", "hotshot-task-impls", "hotshot-types", - "jf-primitives 0.4.4", + "jf-signature", + "jf-vid", "lru 0.12.3", "portpicker", "rand 0.8.5", "serde", "sha2 0.10.8", "sha3", - "snafu 0.8.2", - "tagged-base64 0.4.0", + "snafu 0.8.3", + "tagged-base64", "tide-disco", "tokio", "tracing", "vbs", + "vec1", ] [[package]] name = "hotshot-types" version = "0.1.11" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "anyhow", "ark-bls12-381", @@ -4473,7 +4462,7 @@ dependencies = [ "bincode", "bitvec", "blake3", - "cdn-proto 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.4)", + "cdn-proto", "committable", "custom_debug 0.5.1", "derivative", @@ -4485,42 +4474,25 @@ dependencies = [ "ethereum-types", "futures", "generic-array", - "jf-plonk", - "jf-primitives 0.4.4", - "jf-utils 0.4.4", + "jf-pcs", + "jf-signature", + "jf-utils", + "jf-vid", "lazy_static", "memoize", "rand 0.8.5", "rand_chacha 0.3.1", "serde", "sha2 0.10.8", - "snafu 0.8.2", - "tagged-base64 0.4.0", + "snafu 0.8.3", + "tagged-base64", "time 0.3.36", "tokio", "tracing", "typenum", "url", "vbs", -] - -[[package]] -name = "hotshot-web-server" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" -dependencies = [ - "async-compatibility-layer", - "async-lock 2.8.0", - "async-std", - "clap", - "futures", - "hotshot-types", - "rand 0.8.5", - "tide-disco", - "tokio", - "toml", - "tracing", - "vbs", + "vec1", ] [[package]] @@ -4568,12 +4540,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes 1.6.0", - "futures-core", + "futures-util", "http 1.1.0", "http-body 1.0.0", "pin-project-lite 0.2.14", @@ -4617,9 +4589,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -4635,9 +4607,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes 1.6.0", "futures-channel", @@ -4666,7 +4638,7 @@ dependencies = [ "bytes 1.6.0", "futures-channel", "futures-util", - "h2 0.4.4", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -4685,10 +4657,27 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.29", "rustls 0.21.12", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls 0.23.10", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", ] [[package]] @@ -4697,7 +4686,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.28", + "hyper 0.14.29", "pin-project-lite 0.2.14", "tokio", "tokio-io-timeout", @@ -4721,9 +4710,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" dependencies = [ "bytes 1.6.0", "futures-channel", @@ -4750,7 +4739,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -4804,7 +4793,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" dependencies = [ - "async-io 2.3.2", + "async-io 2.3.3", "core-foundation", "fnv", "futures", @@ -4829,7 +4818,7 @@ dependencies = [ "bytes 1.6.0", "futures", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.29", "log", "rand 0.8.5", "tokio", @@ -4877,18 +4866,18 @@ dependencies = [ [[package]] name = "include_dir" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ "proc-macro2", "quote", @@ -4948,9 +4937,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -4961,7 +4950,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -4984,6 +4973,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "isahc" version = "0.9.14" @@ -5050,126 +5045,111 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] -name = "jf-plonk" -version = "0.4.4" -source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.4#8f48813ca52d964090dbf0de62f07f5e0c7e22c6" +name = "jf-commitment" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-std", +] + +[[package]] +name = "jf-crhf" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-serialize", + "ark-std", +] + +[[package]] +name = "jf-merkle-tree" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" dependencies = [ + "ark-bls12-377", + "ark-bls12-381", + "ark-bn254", "ark-ec", "ark-ff", - "ark-poly", "ark-serialize", "ark-std", "derivative", + "digest 0.10.7", "displaydoc", - "downcast-rs", - "dyn-clone 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "espresso-systems-common 0.4.0", "hashbrown 0.14.5", "itertools 0.12.1", - "jf-primitives 0.4.4", - "jf-relation 0.4.4", - "jf-utils 0.4.4", - "merlin", + "jf-relation", + "jf-rescue", + "jf-utils", "num-bigint", - "rand_chacha 0.3.1", - "rayon", + "num-traits", "serde", "sha3", - "tagged-base64 0.4.0", + "tagged-base64", ] [[package]] -name = "jf-primitives" -version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.2#f85f9024ef42ab8be95b2c81bd15fd6cbadeafbf" +name = "jf-pcs" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" dependencies = [ - "anyhow", - "ark-bls12-377", - "ark-bls12-381", - "ark-bn254", - "ark-bw6-761", - "ark-crypto-primitives", "ark-ec", - "ark-ed-on-bls12-377", - "ark-ed-on-bls12-381", - "ark-ed-on-bn254", "ark-ff", - "ark-pallas", "ark-poly", "ark-serialize", "ark-std", - "blst", - "chacha20poly1305", - "crypto_kx", "derivative", - "digest 0.10.7", "displaydoc", - "espresso-systems-common 0.4.0", - "generic-array", - "hashbrown 0.14.5", "itertools 0.12.1", - "jf-relation 0.4.0-pre.0", - "jf-utils 0.4.0-pre.0", + "jf-utils", "merlin", - "num-bigint", - "num-traits", - "rand_chacha 0.3.1", - "serde", - "sha2 0.10.8", - "sha3", - "tagged-base64 0.3.4", - "typenum", - "zeroize", + "rayon", ] [[package]] -name = "jf-primitives" +name = "jf-plonk" version = "0.4.4" -source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.4#8f48813ca52d964090dbf0de62f07f5e0c7e22c6" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" dependencies = [ - "anyhow", - "ark-bls12-377", - "ark-bls12-381", - "ark-bn254", - "ark-bw6-761", - "ark-crypto-primitives", "ark-ec", - "ark-ed-on-bls12-377", - "ark-ed-on-bls12-381", - "ark-ed-on-bn254", "ark-ff", - "ark-pallas", "ark-poly", "ark-serialize", "ark-std", - "blst", - "chacha20poly1305", - "crypto_kx", "derivative", - "digest 0.10.7", "displaydoc", + "downcast-rs", + "dyn-clone 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "espresso-systems-common 0.4.0", - "generic-array", "hashbrown 0.14.5", "itertools 0.12.1", - "jf-relation 0.4.4", - "jf-utils 0.4.4", + "jf-crhf", + "jf-pcs", + "jf-relation", + "jf-rescue", + "jf-utils", "merlin", "num-bigint", - "num-traits", "rand_chacha 0.3.1", "rayon", "serde", - "sha2 0.10.8", "sha3", - "tagged-base64 0.4.0", - "zeroize", + "tagged-base64", +] + +[[package]] +name = "jf-prf" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-serialize", + "ark-std", ] [[package]] name = "jf-relation" -version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.2#f85f9024ef42ab8be95b2c81bd15fd6cbadeafbf" +version = "0.4.4" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" dependencies = [ "ark-bls12-377", "ark-bls12-381", @@ -5186,66 +5166,108 @@ dependencies = [ "dyn-clone 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.14.5", "itertools 0.12.1", - "jf-utils 0.4.0-pre.0", + "jf-utils", "num-bigint", "rand_chacha 0.3.1", + "rayon", ] [[package]] -name = "jf-relation" -version = "0.4.4" -source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.4#8f48813ca52d964090dbf0de62f07f5e0c7e22c6" +name = "jf-rescue" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" dependencies = [ "ark-bls12-377", "ark-bls12-381", "ark-bn254", "ark-bw6-761", + "ark-crypto-primitives", + "ark-ed-on-bls12-377", + "ark-ed-on-bls12-381", + "ark-ed-on-bn254", + "ark-ff", + "ark-std", + "displaydoc", + "itertools 0.12.1", + "jf-commitment", + "jf-crhf", + "jf-prf", + "jf-relation", + "jf-utils", +] + +[[package]] +name = "jf-signature" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-bls12-381", + "ark-bn254", "ark-ec", "ark-ff", - "ark-poly", "ark-serialize", "ark-std", + "blst", "derivative", + "digest 0.10.7", "displaydoc", - "downcast-rs", - "dyn-clone 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.14.5", "itertools 0.12.1", - "jf-utils 0.4.4", + "jf-crhf", + "jf-relation", + "jf-rescue", + "jf-utils", "num-bigint", - "rand_chacha 0.3.1", - "rayon", + "num-traits", + "serde", + "sha3", + "tagged-base64", + "zeroize", ] [[package]] name = "jf-utils" -version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.2#f85f9024ef42ab8be95b2c81bd15fd6cbadeafbf" +version = "0.4.4" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" dependencies = [ "ark-ec", + "ark-ed-on-bls12-377", + "ark-ed-on-bls12-381", "ark-ff", + "ark-poly", "ark-serialize", "ark-std", "digest 0.10.7", + "displaydoc", + "rand_chacha 0.3.1", + "rayon", "serde", "sha2 0.10.8", - "tagged-base64 0.3.4", + "tagged-base64", ] [[package]] -name = "jf-utils" -version = "0.4.4" -source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.4#8f48813ca52d964090dbf0de62f07f5e0c7e22c6" +name = "jf-vid" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" dependencies = [ + "anyhow", "ark-ec", "ark-ff", + "ark-poly", "ark-serialize", "ark-std", + "derivative", "digest 0.10.7", + "displaydoc", + "generic-array", + "itertools 0.12.1", + "jf-merkle-tree", + "jf-pcs", + "jf-utils", "rayon", "serde", - "sha2 0.10.8", - "tagged-base64 0.4.0", + "tagged-base64", ] [[package]] @@ -5346,7 +5368,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "string_cache", "term", "tiny-keccak", @@ -5360,7 +5382,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.6", + "regex-automata 0.4.7", ] [[package]] @@ -5374,9 +5396,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" @@ -5404,7 +5426,7 @@ dependencies = [ "either", "futures", "futures-timer", - "getrandom 0.2.14", + "getrandom 0.2.15", "instant", "libp2p-allow-block-list", "libp2p-autonat", @@ -5565,7 +5587,7 @@ dependencies = [ "fnv", "futures", "futures-ticker", - "getrandom 0.2.14", + "getrandom 0.2.15", "hex_fmt", "instant", "libp2p-core", @@ -5585,9 +5607,9 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.44.1" +version = "0.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20499a945d2f0221fdc6269b3848892c0f370d2ee3e19c7f65a29d8f860f6126" +checksum = "b5d635ebea5ca0c3c3e77d414ae9b67eccf2a822be06091b9c1a0d13029a1e2f" dependencies = [ "asynchronous-codec 0.7.0", "either", @@ -5608,9 +5630,9 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999ec70441b2fb35355076726a6bc466c932e9bdc66f6a11c6c0aa17c7ab9be0" +checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" dependencies = [ "asn1_der", "bs58", @@ -5663,7 +5685,7 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49007d9a339b3e1d7eeebc4d67c05dbf23d300b7d091193ec2d3f26802d7faf2" dependencies = [ - "async-io 2.3.2", + "async-io 2.3.3", "async-std", "data-encoding", "futures", @@ -5702,14 +5724,15 @@ dependencies = [ [[package]] name = "libp2p-networking" -version = "0.5.43" -source = "git+https://github.com/EspressoSystems/hotshot?tag=0.5.46#f02711daca9ce18ecc86443fa054cafff76464f0" +version = "0.5.58" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.59#b9329c30b25dcbda5018a36ee06ee4294111778f" dependencies = [ "anyhow", "async-compatibility-layer", "async-lock 2.8.0", "async-std", "async-trait", + "bincode", "blake3", "custom_debug 0.5.1", "derive_builder", @@ -5724,11 +5747,10 @@ dependencies = [ "serde", "serde_bytes", "serde_json", - "snafu 0.8.2", + "snafu 0.8.3", "tokio", "tokio-stream", "tracing", - "vbs", "void", ] @@ -5760,9 +5782,9 @@ dependencies = [ [[package]] name = "libp2p-ping" -version = "0.44.0" +version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b94ee41bd8c294194fe608851e45eb98de26fe79bc7913838cbffbfe8c7ce2" +checksum = "a1de5a6cf64fba7f7e8f2102711c9c6c043a8e56b86db8cd306492c517da3fb3" dependencies = [ "either", "futures", @@ -5808,9 +5830,9 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0375cdfee57b47b313ef1f0fdb625b78aed770d33a40cf1c294a371ff5e6666" +checksum = "c67296ad4e092e23f92aea3d2bdb6f24eab79c0929ed816dfb460ea2f4567d2b" dependencies = [ "async-std", "bytes 1.6.0", @@ -5823,8 +5845,8 @@ dependencies = [ "parking_lot", "quinn", "rand 0.8.5", - "ring 0.16.20", - "rustls 0.21.12", + "ring 0.17.8", + "rustls 0.23.10", "socket2 0.5.7", "thiserror", "tokio", @@ -5833,9 +5855,9 @@ dependencies = [ [[package]] name = "libp2p-relay" -version = "0.17.1" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aadb213ffc8e1a6f2b9c48dcf0fc07bf370f2ea4db7981813d45e50671c8d9d" +checksum = "4d1c667cfabf3dd675c8e3cea63b7b98434ecf51721b7894cbb01d29983a6a9b" dependencies = [ "asynchronous-codec 0.7.0", "bytes 1.6.0", @@ -5843,7 +5865,6 @@ dependencies = [ "futures", "futures-bounded", "futures-timer", - "instant", "libp2p-core", "libp2p-identity", "libp2p-swarm", @@ -5854,6 +5875,7 @@ dependencies = [ "thiserror", "tracing", "void", + "web-time", ] [[package]] @@ -5882,9 +5904,9 @@ dependencies = [ [[package]] name = "libp2p-request-response" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12823250fe0c45bdddea6eefa2be9a609aff1283ff4e1d8a294fdbb89572f6f" +checksum = "c314fe28368da5e3a262553fb0ad575c1c8934c461e10de10265551478163836" dependencies = [ "async-trait", "cbor4ii", @@ -5904,9 +5926,9 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.44.1" +version = "0.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e92532fc3c4fb292ae30c371815c9b10103718777726ea5497abc268a4761866" +checksum = "80cae6cb75f89dbca53862f9ebe0b9f463aa7b302762fcfaafb9e51dcc9b0f7e" dependencies = [ "async-std", "either", @@ -5917,6 +5939,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm-derive", + "lru 0.12.3", "multistream-select", "once_cell", "rand 0.8.5", @@ -5928,14 +5951,14 @@ dependencies = [ [[package]] name = "libp2p-swarm-derive" -version = "0.34.1" +version = "0.34.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b644268b4acfdaa6a6100b31226ee7a36d96ab4c43287d113bfd2308607d8b6f" +checksum = "5daceb9dd908417b6dfcfe8e94098bc4aac54500c282e78120b885dadc09b999" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -5958,17 +5981,17 @@ dependencies = [ [[package]] name = "libp2p-tls" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ce7e3c2e7569d685d08ec795157981722ff96e9e9f9eae75df3c29d02b07a5" +checksum = "251b17aebdd29df7e8f80e4d94b782fae42e934c49086e1a81ba23b60a8314f2" dependencies = [ "futures", - "futures-rustls", + "futures-rustls 0.26.0", "libp2p-core", "libp2p-identity", "rcgen 0.11.3", - "ring 0.16.20", - "rustls 0.21.12", + "ring 0.17.8", + "rustls 0.23.10", "rustls-webpki 0.101.7", "thiserror", "x509-parser", @@ -5988,9 +6011,9 @@ dependencies = [ [[package]] name = "libp2p-upnp" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49cc89949bf0e06869297cd4fe2c132358c23fe93e76ad43950453df4da3d35" +checksum = "cccf04b0e3ff3de52d07d5fd6c3b061d0e7f908ffc683c32d9638caedce86fc8" dependencies = [ "futures", "futures-timer", @@ -6010,7 +6033,7 @@ checksum = "f4846d51afd08180e164291c3754ba30dd4fbac6fac65571be56403c16431a5e" dependencies = [ "either", "futures", - "futures-rustls", + "futures-rustls 0.24.0", "libp2p-core", "libp2p-identity", "parking_lot", @@ -6034,7 +6057,7 @@ dependencies = [ "thiserror", "tracing", "yamux 0.12.1", - "yamux 0.13.1", + "yamux 0.13.3", ] [[package]] @@ -6108,9 +6131,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -6132,9 +6155,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "local-ip-address" @@ -6246,7 +6269,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -6261,9 +6284,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoize" @@ -6324,9 +6347,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -6348,33 +6371,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b8f3a258db515d5e91a904ce4ae3f73e091149b90cadbdb93d210bee07f63b" -[[package]] -name = "mockall" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" -dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.60", -] - [[package]] name = "multiaddr" version = "0.18.1" @@ -6436,16 +6432,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -6592,13 +6587,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -6621,21 +6629,19 @@ dependencies = [ ] [[package]] -name = "num-conv" -version = "0.1.0" +name = "num-complex" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] [[package]] -name = "num-derive" -version = "0.4.2" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" @@ -6648,20 +6654,31 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -6673,7 +6690,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -6692,10 +6709,19 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", ] [[package]] @@ -6709,9 +6735,9 @@ dependencies = [ [[package]] name = "oid-registry" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" dependencies = [ "asn1-rs", ] @@ -6776,7 +6802,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -6827,9 +6853,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec", "bitvec", @@ -6841,11 +6867,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", @@ -6859,9 +6885,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -6875,7 +6901,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -6893,9 +6919,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "path-slash" @@ -6946,7 +6972,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "serde", ] @@ -6967,9 +6993,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -6978,9 +7004,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -6988,22 +7014,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "pest_meta" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", @@ -7012,9 +7038,9 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap 2.2.6", @@ -7060,7 +7086,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -7098,7 +7124,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -7127,9 +7153,9 @@ checksum = "d15b6607fa632996eb8a17c9041cb6071cb75ac057abd45dece578723ea8c7c0" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" dependencies = [ "atomic-waker", "fastrand 2.1.0", @@ -7163,12 +7189,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - [[package]] name = "polling" version = "2.8.0" @@ -7187,13 +7207,13 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi", + "hermit-abi 0.4.0", "pin-project-lite 0.2.14", "rustix 0.38.34", "tracing", @@ -7308,39 +7328,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] -name = "predicates" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" -dependencies = [ - "anstyle", - "predicates-core", -] - -[[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" +name = "pretty_assertions" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "predicates-core", - "termtree", + "diff", + "yansi", ] [[package]] name = "prettyplease" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -7349,31 +7353,12 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "uint", ] [[package]] @@ -7416,18 +7401,18 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", @@ -7458,7 +7443,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -7473,15 +7458,15 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "unarray", ] [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes 1.6.0", "prost-derive", @@ -7489,22 +7474,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "prost-types" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ "prost", ] @@ -7578,11 +7563,11 @@ dependencies = [ [[package]] name = "quinn" -version = "0.10.2" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" +checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" dependencies = [ - "async-io 1.13.0", + "async-io 2.3.3", "async-std", "bytes 1.6.0", "futures-io", @@ -7590,7 +7575,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.21.12", + "rustls 0.23.10", "thiserror", "tokio", "tracing", @@ -7598,15 +7583,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.10.6" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" +checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" dependencies = [ "bytes 1.6.0", "rand 0.8.5", - "ring 0.16.20", + "ring 0.17.8", "rustc-hash", - "rustls 0.21.12", + "rustls 0.23.10", "slab", "thiserror", "tinyvec", @@ -7615,15 +7600,15 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" +checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" dependencies = [ - "bytes 1.6.0", "libc", + "once_cell", "socket2 0.5.7", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7700,7 +7685,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] @@ -7765,12 +7750,13 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48406db8ac1f3cbc7dcdb56ec355343817958a356ff430259bb07baf7607e1e1" +checksum = "54077e1872c46788540de1ea3d7f4ccb1983d12f9aa909b234468676c1a36779" dependencies = [ "pem 3.0.4", "ring 0.17.8", + "rustls-pki-types", "time 0.3.36", "x509-parser", "yasna", @@ -7778,9 +7764,9 @@ dependencies = [ [[package]] name = "redis" -version = "0.24.0" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c580d9cbbe1d1b479e8d67cf9daf6a62c957e6846048408b80b43ac3f6af84cd" +checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec" dependencies = [ "arc-swap", "async-trait", @@ -7809,9 +7795,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -7822,7 +7808,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "libredox", "thiserror", ] @@ -7869,19 +7855,19 @@ dependencies = [ "quote", "refinery-core", "regex", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -7895,13 +7881,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -7912,9 +7898,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rend" @@ -7939,8 +7925,8 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", - "hyper-rustls", + "hyper 0.14.29", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", @@ -7953,10 +7939,10 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -7968,20 +7954,21 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "bytes 1.6.0", "encoding_rs", "futures-core", "futures-util", - "h2 0.4.4", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.0", "http-body-util", "hyper 1.3.1", + "hyper-rustls 0.27.2", "hyper-tls", "hyper-util", "ipnet", @@ -7996,7 +7983,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.1", "system-configuration", "tokio", "tokio-native-tls", @@ -8051,7 +8038,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -8194,9 +8181,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -8225,7 +8212,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.22", + "semver 1.0.23", ] [[package]] @@ -8260,7 +8247,7 @@ dependencies = [ "bitflags 2.5.0", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] @@ -8298,7 +8285,21 @@ dependencies = [ "log", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls" +version = "0.23.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +dependencies = [ + "once_cell", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.4", "subtle", "zeroize", ] @@ -8318,15 +8319,15 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" @@ -8340,9 +8341,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.3" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -8351,9 +8352,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rw-stream-sink" @@ -8368,9 +8369,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "salsa20" @@ -8392,9 +8393,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.2" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "cfg-if", "derive_more", @@ -8404,11 +8405,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.2" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", @@ -8489,11 +8490,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -8502,9 +8503,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -8521,9 +8522,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -8561,13 +8562,13 @@ dependencies = [ "async-once-cell", "async-std", "async-trait", - "base64 0.22.0", + "base64 0.22.1", "base64-bytes", "bincode", "blake3", "bytesize", - "cdn-broker 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.6)", - "cdn-marshal 0.1.0 (git+https://github.com/EspressoSystems/Push-CDN?tag=0.2.6)", + "cdn-broker", + "cdn-marshal", "clap", "cld", "committable", @@ -8576,11 +8577,14 @@ dependencies = [ "derive_more", "dotenvy", "es-version", + "escargot", "espresso-macros", "ethers", "ethers-contract-derive", + "fluent-asserter", "futures", "hotshot", + "hotshot-contract-adapter", "hotshot-events-service", "hotshot-orchestrator", "hotshot-query-service", @@ -8589,26 +8593,35 @@ dependencies = [ "hotshot-task", "hotshot-testing", "hotshot-types", - "hotshot-web-server", "include_dir", "itertools 0.12.1", - "jf-primitives 0.4.4", - "jf-utils 0.4.4", - "lazy_static", + "jf-crhf", + "jf-merkle-tree", + "jf-rescue", + "jf-signature", + "jf-utils", + "jf-vid", + "libp2p", "num-traits", + "num_enum", + "paste", "portpicker", + "pretty_assertions", "rand 0.8.5", "rand_chacha 0.3.1", "rand_distr", + "reqwest 0.12.5", "sequencer-utils", "serde", "serde_json", "sha2 0.10.8", - "snafu 0.8.2", + "snafu 0.8.3", + "static_assertions", "strum", "surf-disco", - "tagged-base64 0.4.0", + "tagged-base64", "tempfile", + "thiserror", "tide-disco", "time 0.3.36", "tokio-postgres", @@ -8616,10 +8629,11 @@ dependencies = [ "tracing", "tracing-subscriber 0.3.18", "trait-set", - "trait-variant", "typenum", "url", "vbs", + "vec1", + "vergen", "zeroize", ] @@ -8638,6 +8652,7 @@ dependencies = [ "futures", "hotshot-contract-adapter", "portpicker", + "serde", "serde_json", "surf", "tempfile", @@ -8647,9 +8662,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.199" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -8676,13 +8691,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -8696,9 +8711,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -8718,9 +8733,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -8743,7 +8758,7 @@ version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", @@ -8764,17 +8779,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.60", -] - -[[package]] -name = "serdect" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" -dependencies = [ - "base16ct", - "serde", + "syn 2.0.67", ] [[package]] @@ -9015,11 +9020,11 @@ dependencies = [ [[package]] name = "snafu" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75976f4748ab44f6e5332102be424e7c2dc18daeaf7e725f2040c3ebb133512e" +checksum = "418b8136fec49956eba89be7da2847ec1909df92a9ae4178b5ff0ff092c8d95e" dependencies = [ - "snafu-derive 0.8.2", + "snafu-derive 0.8.3", ] [[package]] @@ -9036,14 +9041,14 @@ dependencies = [ [[package]] name = "snafu-derive" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b19911debfb8c2fb1107bc6cb2d61868aaf53a988449213959bb1b5b1ed95f" +checksum = "1a4812a669da00d17d8266a0439eddcacbc88b17f732f927e52eeb9d196f7fb5" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -9157,11 +9162,10 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" dependencies = [ - "itertools 0.12.1", "nom", "unicode_categories", ] @@ -9443,21 +9447,15 @@ dependencies = [ [[package]] name = "stringprep" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "finl_unicode", "unicode-bidi", "unicode-normalization", + "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -9475,22 +9473,22 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5" [[package]] name = "surf" @@ -9503,7 +9501,7 @@ dependencies = [ "cfg-if", "encoding_rs", "futures-util", - "getrandom 0.2.14", + "getrandom 0.2.15", "http-client", "http-types", "log", @@ -9517,16 +9515,16 @@ dependencies = [ [[package]] name = "surf-disco" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b649b5919e809b105a8ae833154430f7069b0e3a392fff26a672211c6b8e30d" +checksum = "55c871338479a1c27b5e2ae15e5ae0fe70769306d2ef5a0c2d30896a09ac8a3c" dependencies = [ "async-std", "async-tungstenite", "derivative", "futures", "hex", - "reqwest 0.12.4", + "reqwest 0.12.5", "serde", "serde_json", "tide-disco", @@ -9623,7 +9621,7 @@ dependencies = [ "hex", "once_cell", "reqwest 0.11.27", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "sha2 0.10.8", @@ -9645,9 +9643,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" dependencies = [ "proc-macro2", "quote", @@ -9660,6 +9658,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "synstructure" version = "0.12.6" @@ -9680,7 +9684,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -9704,21 +9708,6 @@ dependencies = [ "libc", ] -[[package]] -name = "tagged-base64" -version = "0.3.4" -source = "git+https://github.com/EspressoSystems/tagged-base64?tag=0.3.4#93be6f0f5c0ec8458f13dede3d2b68dcce12a608" -dependencies = [ - "ark-serialize", - "ark-std", - "base64 0.21.7", - "crc-any", - "serde", - "snafu 0.7.5", - "tagged-base64-macros 0.3.3", - "wasm-bindgen", -] - [[package]] name = "tagged-base64" version = "0.4.0" @@ -9727,23 +9716,14 @@ checksum = "7b74bbf1db405a3fd2c6f8cd403bfa14727faa145925efe3012fa270b61551f1" dependencies = [ "ark-serialize", "ark-std", - "base64 0.22.0", + "base64 0.22.1", "crc-any", "serde", - "snafu 0.8.2", - "tagged-base64-macros 0.4.0", + "snafu 0.8.3", + "tagged-base64-macros", "wasm-bindgen", ] -[[package]] -name = "tagged-base64-macros" -version = "0.3.3" -source = "git+https://github.com/EspressoSystems/tagged-base64?tag=0.3.4#93be6f0f5c0ec8458f13dede3d2b68dcce12a608" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "tagged-base64-macros" version = "0.4.0" @@ -9783,30 +9763,24 @@ dependencies = [ "winapi", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -9851,11 +9825,13 @@ dependencies = [ [[package]] name = "tide-disco" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf3579d6f1362fffc7d1686ed061537afe8a187fd0b18e0f5694ad17e977ff3" +checksum = "715558234f09576333f92ee769bd160e6c4bb1d156d25613cf62be9a592f291a" dependencies = [ "anyhow", + "async-h1", + "async-lock 3.4.0", "async-std", "async-trait", "clap", @@ -9873,23 +9849,21 @@ dependencies = [ "libc", "markdown", "maud", - "num-derive", - "num-traits", "parking_lot", "prometheus", - "reqwest 0.12.4", + "reqwest 0.12.5", "routefinder", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "serde_with", "shellexpand", "signal-hook", "signal-hook-async-std", - "snafu 0.8.2", + "snafu 0.8.3", "strum", "strum_macros", - "tagged-base64 0.4.0", + "tagged-base64", "tide", "tide-websockets", "toml", @@ -9943,7 +9917,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", @@ -10015,9 +9991,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes 1.6.0", @@ -10045,13 +10021,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -10111,6 +10087,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.10", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -10132,68 +10119,45 @@ dependencies = [ "log", "rustls 0.21.12", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tungstenite 0.20.1", "webpki-roots 0.25.4", ] [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes 1.6.0", "futures-core", "futures-sink", "pin-project-lite 0.2.14", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit 0.22.14", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.21.1" @@ -10207,15 +10171,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.7", + "winnow 0.6.13", ] [[package]] @@ -10232,7 +10196,34 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.29", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.21.7", + "bytes 1.6.0", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", "hyper-timeout", "percent-encoding", "pin-project", @@ -10297,7 +10288,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -10412,7 +10403,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -10462,6 +10453,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typenum" version = "1.17.0" @@ -10522,6 +10519,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -10594,22 +10597,22 @@ version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "flate2", "log", "once_cell", "rustls 0.22.4", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki 0.102.4", "url", - "webpki-roots 0.26.1", + "webpki-roots 0.26.3", ] [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna 0.5.0", @@ -10631,9 +10634,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -10641,7 +10644,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "serde", ] @@ -10659,9 +10662,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -10669,9 +10672,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc35703541cbccb5278ef7b589d79439fc808ff0b5867195a3230f9a47421d39" +checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b" dependencies = [ "erased-serde", "serde", @@ -10680,9 +10683,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "285b43c29d0b4c0e65aad24561baee67a1b69dc9be9375d4a85138cbf556f7f8" +checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b" dependencies = [ "sval", "sval_buffer", @@ -10695,9 +10698,9 @@ dependencies = [ [[package]] name = "vbs" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "971bcbd6c41e4e53fa9eab95b2a8ddaf1826be2f3e86b4101efebc8c3a17fde0" +checksum = "6a9d83afb293d109ae418271619046cd9d87d63ba83e473e25a5e36101edc453" dependencies = [ "anyhow", "bincode", @@ -10712,6 +10715,27 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec1" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab68b56840f69efb0fefbe3ab6661499217ffdc58e2eef7c3f6f69835386322" +dependencies = [ + "serde", +] + +[[package]] +name = "vergen" +version = "8.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e27d6bdd219887a9eadd19e1c34f32e47fa332301184935c6d9bca26f3cca525" +dependencies = [ + "anyhow", + "cfg-if", + "rustversion", + "time 0.3.36", +] + [[package]] name = "version_check" version = "0.9.4" @@ -10726,9 +10750,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "waker-fn" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "walkdir" @@ -10760,7 +10784,7 @@ dependencies = [ "futures-util", "headers", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.29", "log", "mime", "mime_guess", @@ -10817,7 +10841,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", "wasm-bindgen-shared", ] @@ -10851,7 +10875,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10872,6 +10896,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki" version = "0.21.4" @@ -10899,9 +10933,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] @@ -10960,7 +10994,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ - "windows-core", + "windows-core 0.51.1", "windows-targets 0.48.5", ] @@ -10973,6 +11007,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -11123,9 +11166,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -11192,9 +11235,9 @@ dependencies = [ [[package]] name = "x509-parser" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" dependencies = [ "asn1-rs", "data-encoding", @@ -11202,7 +11245,7 @@ dependencies = [ "lazy_static", "nom", "oid-registry", - "ring 0.16.20", + "ring 0.17.8", "rusticata-macros", "thiserror", "time 0.3.36", @@ -11249,18 +11292,18 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1d0148b89300047e72994bee99ecdabd15a9166a7b70c8b8c37c314dcc9002" +checksum = "a31b5e376a8b012bee9c423acdbb835fc34d45001cfa3106236a624e4b738028" dependencies = [ "futures", - "instant", "log", "nohash-hasher", "parking_lot", "pin-project", "rand 0.8.5", "static_assertions", + "web-time", ] [[package]] @@ -11280,29 +11323,29 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -11315,7 +11358,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.67", ] [[package]] @@ -11359,9 +11402,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.11+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 2dd81d2c6..f6f47b046 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ async-std = { version = "1.12.0", features = ["attributes", "tokio1"] } async-trait = "0.1" base64 = "0.22" base64-bytes = "0.1" +bincode = "1.3.3" blake3 = "1.5" clap = { version = "4.4", features = ["derive", "env", "string"] } cld = "0.5" @@ -46,56 +47,77 @@ dotenvy = "0.15" ethers = { version = "2.0", features = ["solc"] } futures = "0.3" +hotshot = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.59" } # Hotshot imports -hotshot = { git = "https://github.com/EspressoSystems/hotshot", tag = "0.5.46" } -hotshot-builder-api = { git = "https://github.com/EspressoSystems/HotShot.git", tag = "0.5.46" } -hotshot-builder-core = { git = "https://github.com/EspressoSystems/hotshot-builder-core", tag = "0.1.14" } -hotshot-orchestrator = { git = "https://github.com/EspressoSystems/hotshot", tag = "0.5.46" } -hotshot-query-service = { git = "https://github.com/EspressoSystems/hotshot-query-service", tag = "0.1.14" } -hotshot-stake-table = { git = "https://github.com/EspressoSystems/hotshot", tag = "0.5.46" } +hotshot-builder-api = { git = "https://github.com/EspressoSystems/HotShot.git", tag = "rc-0.5.59" } +hotshot-builder-core = { git = "https://github.com/EspressoSystems/hotshot-builder-core", tag = "rc-0.1.29" } +hotshot-events-service = { git = "https://github.com/EspressoSystems/hotshot-events-service.git", tag = "rc-0.1.29" } +hotshot-orchestrator = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.59" } +hotshot-query-service = { git = "https://github.com/EspressoSystems/hotshot-query-service", tag = "rc-0.1.41" } +hotshot-stake-table = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.59" } hotshot-state-prover = { version = "0.1.0", path = "hotshot-state-prover" } -hotshot-task = { git = "https://github.com/EspressoSystems/hotshot", tag = "0.5.46" } -hotshot-testing = { git = "https://github.com/EspressoSystems/hotshot", tag = "0.5.46" } -hotshot-types = { git = "https://github.com/EspressoSystems/hotshot", tag = "0.5.46" } -hotshot-web-server = { git = "https://github.com/EspressoSystems/hotshot", tag = "0.5.46" } -hotshot-events-service = { git = "https://github.com/EspressoSystems/hotshot-events-service.git", tag = "0.1.16" } +hotshot-task = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.59" } +hotshot-testing = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.59" } +hotshot-types = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.59" } +hotshot-contract-adapter = { version = "0.1.0", path = "contracts/rust/adapter" } # Push CDN imports cdn-broker = { git = "https://github.com/EspressoSystems/Push-CDN", features = [ "runtime-async-std", "global-permits", -], tag = "0.2.6", package = "cdn-broker" } +], tag = "0.3.12", package = "cdn-broker" } cdn-marshal = { git = "https://github.com/EspressoSystems/Push-CDN", features = [ "runtime-async-std", "global-permits", -], tag = "0.2.6", package = "cdn-marshal" } +], tag = "0.3.12", package = "cdn-marshal" } -jf-plonk = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.4", features = [ +jf-plonk = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ "test-apis", ] } -jf-primitives = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.4", features = [ +jf-crhf = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5" } +jf-merkle-tree = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ "std", ] } -jf-relation = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.4", features = [ +jf-signature = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ "std", ] } -jf-utils = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.4" } +jf-pcs = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ + "std", + "parallel", +] } +jf-vid = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ + "std", + "parallel", +] } +jf-rescue = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ + "std", + "parallel", +] } +jf-relation = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ + "std", +] } +jf-utils = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5" } +libp2p = { version = "0.53", default-features = false } snafu = "0.8" strum = { version = "0.26", features = ["derive"] } -surf-disco = "0.6" +surf-disco = "0.8" tagged-base64 = "0.4" -tide-disco = "0.6" +tide-disco = "0.8" +thiserror = "1.0.61" time = "0.3" tracing = "0.1" -trait-variant = "0.1" bytesize = "1.3" itertools = "0.12" rand_chacha = "0.3" rand_distr = "0.4" +reqwest = "0.12" serde = { version = "1.0.195", features = ["derive"] } toml = "0.8" url = "2.3" vbs = "0.1" +vec1 = "1.12" +vergen = { version = "8.3", features = ["git", "gitcl"] } zeroize = "1.7" committable = "0.2" portpicker = "0.1.1" +pretty_assertions = "1.4" diff --git a/README.md b/README.md index 2e83826a5..1957da704 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Espresso Sequencer [![Build](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/build.yml/badge.svg)](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/build.yml) +[![Docs](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/doc.yml/badge.svg)](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/doc.yml) [![Contracts](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/contracts.yml/badge.svg)](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/contracts.yml) [![Lint](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/lint.yml/badge.svg)](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/lint.yml) [![Audit](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/audit.yml/badge.svg)](https://github.com/EspressoSystems/espresso-sequencer/actions/workflows/audit.yml) @@ -12,10 +13,38 @@ Consisting of a data availability solution and a decentralized network of nodes rollups can leverage the Espresso Sequencer to give developers and end users fast confirmations, low (and fair) fees, and robust infrastructure. -[Documentation](https://docs.espressosys.com/sequencer/espresso-sequencer-architecture/readme) +[Official Documentation](https://docs.espressosys.com/sequencer/espresso-sequencer-architecture/readme) + +### Architecture + +This diagram below depicts a simplified view of the current architecture of the Espresso Sequencer. The diagram includes +views of an Espresso Sequencer node, the Espresso Sequencer Network (nodes, CDN, builders, prover, state relay service), +two rollups (one ZK rollup "Z", one optimistic rollup "O") that use the Espresso Sequencer for sequencing and some +important L1 contracts. + +- Glossary + - Namespace: an identifier to distinguish rollups, akin to an Ethereum chain ID + - Rollup transaction: an transaction a user submits to a rollup, usually an EVM transaction + - Transaction: a transaction inside the Espresso Sequencer: a rollup transaction plus a namespace ID of the rollup + - Rollup block: a block in a rollup consisting only of transactions in this rollup + - Espresso block: a block produced by the Espresso sequencer containing transactions of multiple rollups ![Architecture diagram](./doc/architecture.svg) +The sequence diagram below serves as a complement to the architecture diagram. The following interactions are depicted. + +1. Builders deposit funds into the fee contract on Ethereum Layer 1. These funds are later used to pay fees. +2. Users submit transactions to the Submit APIs of sequencer nodes. +3. The leader/proposer obtains a block from a builder. +4. HotShot consensus creates new blocks containing sequenced rollup transactions. +5. A rollup produces a rollup block with transactions sequenced by the Espresso sequencer. +6. A proof for a HotShot state update is created and verified in the Light Client smart contract. +7. A ZK rollup proves a correct state transaction by sending a proof to its rollup smart contract. +8. A dispute is settled in an optimistic rollup. If necessary, the HotShot commitment is read from the Light Client + contract. + +![Sequence diagram](./doc/sequence-diagram.svg) + # Running the demo Refer to [sequencer-example-l2](https://github.com/EspressoSystems/sequencer-example-l2) for instructions on how to run @@ -28,6 +57,16 @@ a dockerized Espresso Sequencer network with an example Layer 2 rollup applicati - Activate the environment with `nix-shell`, or `nix develop`, or `direnv allow` if using [direnv](https://direnv.net/). - For installation without nix please see [ubuntu.md](./doc/ubuntu.md). +## Documentation + +The rust code documentation can be found at +[http://sequencer.docs.espressosys.com](http://sequencer.docs.espressosys.com). Please note the disclaimer about API +stability at the end of the readme. + +To generate the documentation locally and view it in the browser, run + + just doc --open + ## Run the tests just pull # to pull docker images diff --git a/builder/Cargo.toml b/builder/Cargo.toml index 4b196d8ef..ec03d40ec 100644 --- a/builder/Cargo.toml +++ b/builder/Cargo.toml @@ -6,6 +6,7 @@ authors = { workspace = true } edition = { workspace = true } [features] +default = ["libp2p"] libp2p = ["sequencer/libp2p"] [dependencies] @@ -30,7 +31,9 @@ hotshot-orchestrator = { workspace = true } hotshot-stake-table = { workspace = true } hotshot-state-prover = { path = "../hotshot-state-prover" } hotshot-types = { workspace = true } -jf-primitives = { workspace = true } +jf-merkle-tree = { workspace = true } +jf-signature = { workspace = true, features = ["bls"] } +libp2p = { workspace = true } portpicker = { workspace = true } rand = "0.8.5" sequencer = { path = "../sequencer", features = ["testing"] } @@ -43,3 +46,7 @@ tide-disco = { workspace = true } tracing = { workspace = true } url = { workspace = true } vbs = { workspace = true } +vec1 = { workspace = true } + +[dev-dependencies] +sequencer = { path = "../sequencer", features = ["testing"] } diff --git a/builder/src/bin/permissioned-builder.rs b/builder/src/bin/permissioned-builder.rs index f3e70cd40..53833f537 100644 --- a/builder/src/bin/permissioned-builder.rs +++ b/builder/src/bin/permissioned-builder.rs @@ -10,9 +10,10 @@ use hotshot_types::light_client::StateSignKey; use hotshot_types::signature_key::BLSPrivKey; use hotshot_types::traits::metrics::NoMetrics; use hotshot_types::traits::node_implementation::ConsensusTime; -use sequencer::eth_signature_key::EthKeyPair; +use libp2p::Multiaddr; use sequencer::persistence::no_storage::NoStorage; -use sequencer::{BuilderParams, L1Params, NetworkParams}; +use sequencer::{eth_signature_key::EthKeyPair, Genesis}; +use sequencer::{L1Params, NetworkParams}; use snafu::Snafu; use std::net::ToSocketAddrs; use std::num::NonZeroUsize; @@ -21,10 +22,6 @@ use url::Url; #[derive(Parser, Clone, Debug)] pub struct PermissionedBuilderOptions { - /// Unique identifier for this instance of the sequencer network. - #[clap(long, env = "ESPRESSO_SEQUENCER_CHAIN_ID", default_value = "0")] - pub chain_id: u16, - /// URL of the HotShot orchestrator. #[clap( short, @@ -46,7 +43,6 @@ pub struct PermissionedBuilderOptions { /// The address to bind to for Libp2p (in `host:port` form) #[clap( - short, long, env = "ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS", default_value = "0.0.0.0:1769" @@ -56,16 +52,26 @@ pub struct PermissionedBuilderOptions { /// The address we advertise to other nodes as being a Libp2p endpoint. /// Should be supplied in `host:port` form. #[clap( - short, long, env = "ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS", default_value = "localhost:1769" )] pub libp2p_advertise_address: String, + /// A comma-separated list of Libp2p multiaddresses to use as bootstrap + /// nodes. + /// + /// Overrides those loaded from the `HotShot` config. + #[clap( + long, + env = "ESPRESSO_SEQUENCER_LIBP2P_BOOTSTRAP_NODES", + value_delimiter = ',', + num_args = 1.. + )] + pub libp2p_bootstrap_nodes: Option>, + /// URL of the Light Client State Relay Server #[clap( - short, long, env = "ESPRESSO_STATE_RELAY_SERVER_URL", default_value = "http://localhost:8083" @@ -83,6 +89,10 @@ pub struct PermissionedBuilderOptions { )] pub webserver_poll_interval: Duration, + /// Path to TOML file containing genesis state. + #[clap(long, name = "GENESIS_FILE", env = "ESPRESSO_BUILDER_GENESIS_FILE")] + pub genesis_file: PathBuf, + /// Path to file containing private keys. /// /// The file should follow the .env format, with two keys: @@ -99,7 +109,7 @@ pub struct PermissionedBuilderOptions { #[clap( long, env = "ESPRESSO_BUILDER_PRIVATE_STAKING_KEY", - conflicts_with = "key_file" + conflicts_with = "KEY_FILE" )] pub private_staking_key: Option, @@ -109,7 +119,7 @@ pub struct PermissionedBuilderOptions { #[clap( long, env = "ESPRESSO_BUILDER_PRIVATE_STATE_KEY", - conflicts_with = "key_file" + conflicts_with = "KEY_FILE" )] pub private_state_key: Option, @@ -144,9 +154,13 @@ pub struct PermissionedBuilderOptions { #[clap(short, long, env = "ESPRESSO_BUILDER_BOOTSTRAPPED_VIEW")] pub view_number: u64, - /// BUILDER CHANNEL CAPACITY - #[clap(long, env = "ESPRESSO_BUILDER_CHANNEL_CAPACITY")] - pub channel_capacity: NonZeroUsize, + /// BUILDER TRANSACTIONS CHANNEL CAPACITY + #[clap(long, env = "ESPRESSO_BUILDER_TX_CHANNEL_CAPACITY")] + pub tx_channel_capacity: NonZeroUsize, + + /// BUILDER HS EVENTS CHANNEL CAPACITY + #[clap(long, env = "ESPRESSO_BUILDER_EVENT_CHANNEL_CAPACITY")] + pub event_channel_capacity: NonZeroUsize, /// Url a sequencer can use to stream hotshot events #[clap(long, env = "ESPRESSO_SEQUENCER_HOTSHOT_EVENTS_PROVIDER")] @@ -164,7 +178,6 @@ pub struct PermissionedBuilderOptions { /// The number of views to buffer before a builder garbage collects its state #[clap( - short, long, env = "ESPRESSO_BUILDER_BUFFER_VIEW_NUM_COUNT", default_value = "15" @@ -174,15 +187,6 @@ pub struct PermissionedBuilderOptions { /// Whether or not we are a DA node. #[clap(long, env = "ESPRESSO_SEQUENCER_IS_DA", action)] pub is_da: bool, - - /// Base Fee for a block - #[clap( - short, - long, - env = "ESPRESSO_BUILDER_BLOCK_BASE_FEE", - default_value = "0" - )] - base_fee: u64, } #[derive(Clone, Debug, Snafu)] @@ -231,14 +235,11 @@ async fn main() -> anyhow::Result<()> { let l1_params = L1Params { url: opt.l1_provider_url, + events_max_block_range: 10000, }; let builder_key_pair = EthKeyPair::from_mnemonic(&opt.eth_mnemonic, opt.eth_account_index)?; - let builder_params = BuilderParams { - prefunded_accounts: vec![], - }; - // Parse supplied Libp2p addresses to their socket form // We expect all nodes to be reachable via IPv4, so we filter out any IPv6 addresses. // Downstream in HotShot we pin the IP address to v4, but this can be fixed in the future. @@ -259,11 +260,13 @@ async fn main() -> anyhow::Result<()> { cdn_endpoint: opt.cdn_endpoint, libp2p_advertise_address, libp2p_bind_address, + libp2p_bootstrap_nodes: opt.libp2p_bootstrap_nodes, orchestrator_url: opt.orchestrator_url, state_relay_server_url: opt.state_relay_server_url, private_staking_key: private_staking_key.clone(), private_state_key, state_peers: opt.state_peers, + catchup_backoff: Default::default(), }; let sequencer_version = SEQUENCER_VERSION; @@ -280,21 +283,21 @@ async fn main() -> anyhow::Result<()> { // it will internally spawn the builder web server let ctx = init_node( + Genesis::from_file(&opt.genesis_file)?, network_params, &NoMetrics, - builder_params, l1_params, builder_server_url.clone(), builder_key_pair, bootstrapped_view, - opt.channel_capacity, + opt.tx_channel_capacity, + opt.event_channel_capacity, sequencer_version, NoStorage, max_api_response_timeout_duration, buffer_view_num_count, opt.is_da, txn_timeout_duration, - opt.base_fee, ) .await?; diff --git a/builder/src/bin/permissionless-builder.rs b/builder/src/bin/permissionless-builder.rs index 1031461e0..35b09a8c4 100644 --- a/builder/src/bin/permissionless-builder.rs +++ b/builder/src/bin/permissionless-builder.rs @@ -3,13 +3,13 @@ use builder::non_permissioned::{build_instance_state, BuilderConfig}; use clap::Parser; use cld::ClDuration; use es_version::SEQUENCER_VERSION; +use hotshot::traits::ValidatedState; use hotshot_types::data::ViewNumber; use hotshot_types::traits::node_implementation::ConsensusTime; -use sequencer::eth_signature_key::EthKeyPair; -use sequencer::L1Params; +use sequencer::{eth_signature_key::EthKeyPair, Genesis, L1Params}; use snafu::Snafu; use std::num::NonZeroUsize; -use std::{str::FromStr, time::Duration}; +use std::{path::PathBuf, str::FromStr, time::Duration}; use url::Url; #[derive(Parser, Clone, Debug)] @@ -50,9 +50,17 @@ struct NonPermissionedBuilderOptions { #[clap(short, long, env = "ESPRESSO_BUILDER_BOOTSTRAPPED_VIEW")] view_number: u64, - /// BUILDER CHANNEL CAPACITY - #[clap(short, long, env = "ESPRESSO_BUILDER_CHANNEL_CAPACITY")] - channel_capacity: NonZeroUsize, + /// BUILDER TRANSACTIONS CHANNEL CAPACITY + #[clap(long, env = "ESPRESSO_BUILDER_TX_CHANNEL_CAPACITY")] + pub tx_channel_capacity: NonZeroUsize, + + /// BUILDER HS EVENTS CHANNEL CAPACITY + #[clap(long, env = "ESPRESSO_BUILDER_EVENT_CHANNEL_CAPACITY")] + pub event_channel_capacity: NonZeroUsize, + + /// NETWORK INITIAL NODE COUNT + #[clap(short, long, env = "ESPRESSO_BUILDER_INIT_NODE_COUNT")] + node_count: NonZeroUsize, /// The amount of time a builder can wait before timing out a request to the API. #[clap( @@ -66,21 +74,15 @@ struct NonPermissionedBuilderOptions { /// The number of views to buffer before a builder garbage collects its state #[clap( - short, long, env = "ESPRESSO_BUILDER_BUFFER_VIEW_NUM_COUNT", default_value = "15" )] buffer_view_num_count: usize, - /// Base Fee for a block - #[clap( - short, - long, - env = "ESPRESSO_BUILDER_BLOCK_BASE_FEE", - default_value = "0" - )] - base_fee: u64, + /// Path to TOML file containing genesis state. + #[clap(long, name = "GENESIS_FILE", env = "ESPRESSO_BUILDER_GENESIS_FILE")] + genesis_file: PathBuf, } #[derive(Clone, Debug, Snafu)] @@ -102,11 +104,13 @@ async fn main() -> anyhow::Result<()> { setup_backtrace(); let opt = NonPermissionedBuilderOptions::parse(); + let genesis = Genesis::from_file(&opt.genesis_file)?; let sequencer_version = SEQUENCER_VERSION; let l1_params = L1Params { url: opt.l1_provider_url, + events_max_block_range: 10000, }; let builder_key_pair = EthKeyPair::from_mnemonic(&opt.eth_mnemonic, opt.eth_account_index)?; @@ -114,8 +118,15 @@ async fn main() -> anyhow::Result<()> { let builder_server_url: Url = format!("http://0.0.0.0:{}", opt.port).parse().unwrap(); - let instance_state = - build_instance_state(l1_params, opt.state_peers, sequencer_version).unwrap(); + let instance_state = build_instance_state( + genesis.chain_config, + l1_params, + opt.state_peers, + sequencer_version, + ) + .unwrap(); + + let validated_state = ValidatedState::genesis(&instance_state).0; let api_response_timeout_duration = opt.max_api_timeout_duration; @@ -127,14 +138,16 @@ async fn main() -> anyhow::Result<()> { let _builder_config = BuilderConfig::init( builder_key_pair, bootstrapped_view, - opt.channel_capacity, + opt.tx_channel_capacity, + opt.event_channel_capacity, + opt.node_count, instance_state, + validated_state, opt.hotshot_event_streaming_url, builder_server_url, api_response_timeout_duration, buffer_view_num_count, txn_timeout_duration, - opt.base_fee, ) .await; diff --git a/builder/src/lib.rs b/builder/src/lib.rs index 8d5a557fe..28714580a 100644 --- a/builder/src/lib.rs +++ b/builder/src/lib.rs @@ -9,10 +9,7 @@ use futures::{ stream::{Stream, StreamExt}, }; use hotshot::{ - traits::{ - election::static_committee::GeneralStaticCommittee, - implementations::{NetworkingMetricsValue, WebServerNetwork}, - }, + traits::election::static_committee::GeneralStaticCommittee, types::{SignatureKey, SystemContextHandle}, HotShotInitializer, Memberships, Networks, SystemContext, }; @@ -22,12 +19,11 @@ use hotshot_orchestrator::{ }; use hotshot_types::{ consensus::ConsensusMetricsValue, - constants::{Version01, STATIC_VER_0_1}, + constants::Base, event::Event, light_client::StateKeyPair, signature_key::{BLSPrivKey, BLSPubKey}, - traits::election::Membership, - traits::metrics::Metrics, + traits::{election::Membership, metrics::Metrics}, HotShotConfig, PeerConfig, ValidatorConfig, }; use std::fmt::Display; @@ -42,10 +38,8 @@ use hotshot_builder_api::builder::{ BuildError, Error as BuilderApiError, Options as HotshotBuilderApiOptions, }; use hotshot_builder_core::service::{GlobalState, ProxyGlobalState}; -use jf_primitives::{ - merkle_tree::{namespaced_merkle_tree::NamespacedMerkleTreeScheme, MerkleTreeScheme}, - signatures::bls_over_bn254::VerKey, -}; +use jf_merkle_tree::{namespaced_merkle_tree::NamespacedMerkleTreeScheme, MerkleTreeScheme}; +use jf_signature::bls_over_bn254::VerKey; use sequencer::catchup::mock::MockStateCatchup; use sequencer::state_signature::StakeTableCommitmentType; use sequencer::{ @@ -57,7 +51,7 @@ use sequencer::{ state::FeeAccount, state::ValidatedState, state_signature::{static_stake_table_commitment, StateSigner}, - BuilderParams, L1Params, NetworkParams, Node, NodeState, PrivKey, PubKey, SeqTypes, + L1Params, NetworkParams, Node, NodeState, PrivKey, PubKey, SeqTypes, }; use std::{alloc::System, any, fmt::Debug, mem}; use std::{marker::PhantomData, net::IpAddr}; @@ -69,25 +63,22 @@ pub mod non_permissioned; pub mod permissioned; // It runs the api service for the builder -pub fn run_builder_api_service(url: Url, source: Arc>>) { +pub fn run_builder_api_service(url: Url, source: ProxyGlobalState) { // it is to serve hotshot - let builder_api = hotshot_builder_api::builder::define_api::< - Arc>>, - SeqTypes, - Version01, - >(&HotshotBuilderApiOptions::default()) - .expect("Failed to construct the builder APIs"); + let builder_api = + hotshot_builder_api::builder::define_api::, SeqTypes, Base>( + &HotshotBuilderApiOptions::default(), + ) + .expect("Failed to construct the builder APIs"); // it enables external clients to submit txn to the builder's private mempool - let private_mempool_api = hotshot_builder_api::builder::submit_api::< - Arc>>, - SeqTypes, - Version01, - >(&HotshotBuilderApiOptions::default()) - .expect("Failed to construct the builder API for private mempool txns"); + let private_mempool_api = + hotshot_builder_api::builder::submit_api::, SeqTypes, Base>( + &HotshotBuilderApiOptions::default(), + ) + .expect("Failed to construct the builder API for private mempool txns"); - let mut app: App>>, BuilderApiError> = - App::with_state(source); + let mut app: App, BuilderApiError> = App::with_state(source); app.register_module("block_info", builder_api) .expect("Failed to register the builder API"); @@ -95,7 +86,7 @@ pub fn run_builder_api_service(url: Url, source: Arc, non_staking_nodes_state_key_pairs: Vec, non_staking_nodes_stake_entries: Vec>, - master_map: Arc, PubKey>>, + master_map: Arc>, anvil: Arc, } @@ -199,7 +190,7 @@ pub mod testing { // Only pass the pub keys to the hotshot config let known_nodes_without_stake_pub_keys = known_nodes_without_stake .iter() - .map(|x| ::get_public_key(&x.stake_table_entry)) + .map(|x| ::public_key(&x.stake_table_entry)) .collect::>(); let master_map = MasterMap::new(); @@ -224,12 +215,16 @@ pub mod testing { data_request_delay: Duration::from_millis(200), view_sync_timeout: Duration::from_secs(5), fixed_leader_for_gpuvid: 0, - builder_url, + builder_urls: vec1::vec1![builder_url], builder_timeout: Duration::from_secs(1), start_threshold: ( known_nodes_with_stake.clone().len() as u64, known_nodes_with_stake.clone().len() as u64, ), + start_proposing_view: 0, + stop_proposing_view: 0, + start_voting_view: 0, + stop_voting_view: 0, }; Self { @@ -265,7 +260,7 @@ pub mod testing { .iter() .zip(&state_key_pairs) .map(|(pub_key, state_key_pair)| PeerConfig:: { - stake_table_entry: pub_key.get_stake_table_entry(stake_value), + stake_table_entry: pub_key.stake_table_entry(stake_value), state_ver_key: state_key_pair.ver_key(), }) .collect::>(); @@ -326,7 +321,7 @@ pub mod testing { bind_version: Ver, options: impl PersistenceOptions, ) -> Vec<( - SystemContextHandle>, + Arc>>, Option>, )> { let num_staked_nodes = self.num_staked_nodes(); @@ -352,7 +347,7 @@ pub mod testing { ) .await; // wrapped in some because need to take later - (hotshot_handle, Some(state_signer)) + (Arc::new(hotshot_handle), Some(state_signer)) } })) .await @@ -382,8 +377,7 @@ pub mod testing { let network = Arc::new(MemoryNetwork::new( config.my_own_validator_config.public_key, - NetworkingMetricsValue::new(metrics), - self.master_map.clone(), + &self.master_map, None, )); let networks = Networks { @@ -393,8 +387,9 @@ pub mod testing { }; let node_state = NodeState::new( + i as u64, ChainConfig::default(), - L1Client::new(self.anvil.endpoint().parse().unwrap(), Address::default()), + L1Client::new(self.anvil.endpoint().parse().unwrap(), 1), MockStateCatchup::default(), ) .with_genesis(ValidatedState::default()); @@ -439,7 +434,7 @@ pub mod testing { let hotshot_events_api = hotshot_events_service::events::define_api::< Arc>>, SeqTypes, - Version01, + Base, >(&EventStreamingApiOptions::default()) .expect("Failed to define hotshot eventsAPI"); @@ -448,14 +443,14 @@ pub mod testing { app.register_module("hotshot-events", hotshot_events_api) .expect("Failed to register hotshot events API"); - async_spawn(app.serve(url, STATIC_VER_0_1)); + async_spawn(app.serve(url, Base::instance())); } // enable hotshot event streaming pub fn enable_hotshot_node_event_streaming( hotshot_events_api_url: Url, known_nodes_with_stake: Vec>, num_non_staking_nodes: usize, - hotshot_context_handle: SystemContextHandle>, + hotshot_context_handle: Arc>>, ) { // create a event streamer let events_streamer = Arc::new(RwLock::new(EventsStreamer::new( @@ -469,7 +464,7 @@ pub mod testing { // send the events to the event streaming state async_spawn({ async move { - let mut hotshot_event_stream = hotshot_context_handle.get_event_stream(); + let mut hotshot_event_stream = hotshot_context_handle.event_stream(); loop { let event = hotshot_event_stream.next().await.unwrap(); tracing::debug!("Before writing in event streamer: {event:?}"); @@ -497,12 +492,12 @@ pub mod testing { if let Decide { leaf_chain, .. } = event.event { if let Some(height) = leaf_chain.iter().find_map(|LeafInfo { leaf, .. }| { if leaf - .get_block_payload() + .block_payload() .as_ref()? - .transaction_commitments(leaf.get_block_header().metadata()) + .transaction_commitments(leaf.block_header().metadata()) .contains(&commitment) { - Some(leaf.get_block_header().block_number()) + Some(leaf.block_header().block_number()) } else { None } @@ -530,10 +525,11 @@ pub mod testing { ) -> Self { // setup the instance state let node_state = NodeState::new( + u64::MAX, ChainConfig::default(), L1Client::new( hotshot_test_config.get_anvil().endpoint().parse().unwrap(), - Address::default(), + 1, ), MockStateCatchup::default(), ) @@ -544,22 +540,27 @@ pub mod testing { let (fee_account, key_pair) = FeeAccount::generated_from_seed_indexed(seed, 2011_u64); // channel capacity for the builder states - let channel_capacity = NonZeroUsize::new(100).unwrap(); + let tx_channel_capacity = NonZeroUsize::new(500).unwrap(); + let event_channel_capacity = NonZeroUsize::new(20).unwrap(); // bootstrapping view number // A new builder can use this view number to start building blocks from this view number let bootstrapped_view = ViewNumber::new(0); + let node_count = NonZeroUsize::new(HotShotTestConfig::total_nodes()).unwrap(); + let builder_config = BuilderConfig::init( key_pair, bootstrapped_view, - channel_capacity, + tx_channel_capacity, + event_channel_capacity, + node_count, node_state, + ValidatedState::default(), hotshot_events_streaming_api_url, hotshot_builder_api_url, Duration::from_millis(2000), 15, - Duration::from_millis(50), - 0, + Duration::from_millis(500), ) .await .unwrap(); @@ -584,17 +585,18 @@ pub mod testing { { pub async fn init_permissioned_builder( hotshot_test_config: HotShotTestConfig, - hotshot_handle: SystemContextHandle>, + hotshot_handle: Arc>>, node_id: u64, state_signer: StateSigner, hotshot_builder_api_url: Url, ) -> Self { // setup the instance state let node_state = NodeState::new( + node_id, ChainConfig::default(), L1Client::new( hotshot_test_config.get_anvil().endpoint().parse().unwrap(), - Address::default(), + 1, ), MockStateCatchup::default(), ) @@ -605,24 +607,26 @@ pub mod testing { let (fee_account, key_pair) = FeeAccount::generated_from_seed_indexed(seed, 2011_u64); // channel capacity for the builder states - let channel_capacity = NonZeroUsize::new(100).unwrap(); + let tx_channel_capacity = NonZeroUsize::new(20).unwrap(); + let event_channel_capacity = NonZeroUsize::new(500).unwrap(); // bootstrapping view number // A new builder can use this view number to start building blocks from this view number let bootstrapped_view = ViewNumber::new(0); let builder_context = BuilderContext::init( - hotshot_handle, + Arc::clone(&hotshot_handle), state_signer, node_id, key_pair, bootstrapped_view, - channel_capacity, + tx_channel_capacity, + event_channel_capacity, node_state, + ValidatedState::default(), hotshot_builder_api_url, Duration::from_millis(2000), 15, - Duration::from_millis(50), - 0, + Duration::from_millis(500), ) .await .unwrap(); @@ -664,10 +668,10 @@ mod test { use hotshot_builder_core::service::GlobalState; use hotshot_types::event::LeafInfo; use hotshot_types::traits::block_contents::{ - vid_commitment, BlockHeader, BlockPayload, GENESIS_VID_NUM_STORAGE_NODES, + vid_commitment, BlockHeader, BlockPayload, EncodeBytes, GENESIS_VID_NUM_STORAGE_NODES, }; use hotshot_types::utils::BuilderCommitment; - use sequencer::block::payload::Payload; + use sequencer::block::Payload; use sequencer::persistence::no_storage::{self, NoStorage}; use sequencer::persistence::sql; use sequencer::{empty_builder_commitment, Header}; @@ -695,23 +699,23 @@ mod test { let total_nodes = HotShotTestConfig::total_nodes(); // try to listen on non-voting node handle as it is the last handle - let mut events = handles[total_nodes - 1].0.get_event_stream(); + let mut events = handles[total_nodes - 1].0.event_stream(); for (handle, ..) in handles.iter() { handle.hotshot.start_consensus().await; } let genesis_state = NodeState::mock(); + let validated_state = ValidatedState::default(); let mut parent = { // TODO refactor repeated code from other tests let (genesis_payload, genesis_ns_table) = - Payload::from_transactions([], Arc::new(genesis_state.clone())) + Payload::from_transactions([], &validated_state, &genesis_state) + .await .expect("unable to create genesis payload"); let builder_commitment = genesis_payload.builder_commitment(&genesis_ns_table); let genesis_commitment = { // TODO we should not need to collect payload bytes just to compute vid_commitment - let payload_bytes = genesis_payload - .encode() - .expect("unable to encode genesis payload"); + let payload_bytes = genesis_payload.encode(); vid_commitment(&payload_bytes, GENESIS_VID_NUM_STORAGE_NODES) }; Header::genesis( @@ -733,7 +737,7 @@ mod test { // Check that each successive header satisfies invariants relative to its parent: all // the fields which should be monotonic are. for LeafInfo { leaf, .. } in leaf_chain.iter().rev() { - let header = leaf.get_block_header().clone(); + let header = leaf.block_header().clone(); if header.height == 0 { parent = header; continue; diff --git a/builder/src/non_permissioned.rs b/builder/src/non_permissioned.rs index 4742b4265..a9e726690 100644 --- a/builder/src/non_permissioned.rs +++ b/builder/src/non_permissioned.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use async_broadcast::{ broadcast, Receiver as BroadcastReceiver, RecvError, Sender as BroadcastSender, TryRecvError, }; @@ -20,21 +21,24 @@ use hotshot_builder_core::{ BuildBlockInfo, BuilderProgress, BuilderState, BuiltFromProposedBlock, MessageType, ResponseMessage, }, - service::{run_non_permissioned_standalone_builder_service, GlobalState, ProxyGlobalState}, + service::{ + run_non_permissioned_standalone_builder_service, GlobalState, ProxyGlobalState, + ReceivedTransaction, + }, }; use hotshot_types::{ - constants::{Version01, STATIC_VER_0_1}, data::{fake_commitment, Leaf, ViewNumber}, traits::{ block_contents::{vid_commitment, GENESIS_VID_NUM_STORAGE_NODES}, node_implementation::{ConsensusTime, NodeType}, + EncodeBytes, }, utils::BuilderCommitment, }; use sequencer::{ - catchup::StatePeers, eth_signature_key::EthKeyPair, l1_client::L1Client, BuilderParams, - ChainConfig, L1Params, NetworkParams, NodeState, Payload, PrivKey, PubKey, SeqTypes, + catchup::StatePeers, eth_signature_key::EthKeyPair, l1_client::L1Client, ChainConfig, L1Params, + NetworkParams, NodeState, Payload, PrivKey, PubKey, SeqTypes, ValidatedState, }; use hotshot_events_service::{ @@ -57,15 +61,20 @@ pub struct BuilderConfig { } pub fn build_instance_state( + chain_config: ChainConfig, l1_params: L1Params, state_peers: Vec, _: Ver, ) -> anyhow::Result { - let l1_client = L1Client::new(l1_params.url, Address::default()); + let l1_client = L1Client::new(l1_params.url, l1_params.events_max_block_range); let instance_state = NodeState::new( - ChainConfig::default(), + u64::MAX, // dummy node ID, only used for debugging + chain_config, l1_client, - Arc::new(StatePeers::::from_urls(state_peers)), + Arc::new(StatePeers::::from_urls( + state_peers, + Default::default(), + )), ); Ok(instance_state) } @@ -75,50 +84,64 @@ impl BuilderConfig { pub async fn init( builder_key_pair: EthKeyPair, bootstrapped_view: ViewNumber, - channel_capacity: NonZeroUsize, + tx_channel_capacity: NonZeroUsize, + event_channel_capacity: NonZeroUsize, + node_count: NonZeroUsize, instance_state: NodeState, + validated_state: ValidatedState, hotshot_events_api_url: Url, hotshot_builder_apis_url: Url, max_api_timeout_duration: Duration, buffered_view_num_count: usize, - maximise_txns_count_timeout_duration: Duration, - base_fee: u64, + maximize_txns_count_timeout_duration: Duration, ) -> anyhow::Result { + tracing::info!( + address = %builder_key_pair.fee_account(), + ?bootstrapped_view, + %tx_channel_capacity, + %event_channel_capacity, + ?max_api_timeout_duration, + buffered_view_num_count, + ?maximize_txns_count_timeout_duration, + "initializing builder", + ); + // tx channel - let (tx_sender, tx_receiver) = broadcast::>(channel_capacity.get()); + let (mut tx_sender, tx_receiver) = + broadcast::>>(tx_channel_capacity.get()); + tx_sender.set_overflow(true); // da channel - let (da_sender, da_receiver) = broadcast::>(channel_capacity.get()); + let (da_sender, da_receiver) = + broadcast::>(event_channel_capacity.get()); // qc channel - let (qc_sender, qc_receiver) = broadcast::>(channel_capacity.get()); + let (qc_sender, qc_receiver) = + broadcast::>(event_channel_capacity.get()); // decide channel let (decide_sender, decide_receiver) = - broadcast::>(channel_capacity.get()); + broadcast::>(event_channel_capacity.get()); // builder api request channel - let (req_sender, req_receiver) = broadcast::>(channel_capacity.get()); - - // builder api response channel - let (res_sender, res_receiver) = unbounded(); + let (req_sender, req_receiver) = + broadcast::>(event_channel_capacity.get()); let (genesis_payload, genesis_ns_table) = - Payload::from_transactions([], Arc::new(instance_state.clone())) + Payload::from_transactions([], &validated_state, &instance_state) + .await .expect("genesis payload construction failed"); + let builder_commitment = genesis_payload.builder_commitment(&genesis_ns_table); + let vid_commitment = { - // TODO we should not need to collect payload bytes just to compute vid_commitment - let payload_bytes = genesis_payload - .encode() - .expect("unable to encode genesis payload"); + let payload_bytes = genesis_payload.encode(); vid_commitment(&payload_bytes, GENESIS_VID_NUM_STORAGE_NODES) }; // create the global state let global_state: GlobalState = GlobalState::::new( req_sender, - res_receiver, tx_sender.clone(), vid_commitment, bootstrapped_view, @@ -127,7 +150,6 @@ impl BuilderConfig { ); let global_state = Arc::new(RwLock::new(global_state)); - let global_state_clone = global_state.clone(); let builder_state = BuilderState::::new( @@ -137,19 +159,23 @@ impl BuilderConfig { leaf_commit: fake_commitment(), builder_commitment, }, - tx_receiver, decide_receiver, da_receiver, qc_receiver, req_receiver, + tx_receiver, + Vec::new() /* tx_queue */, global_state_clone, - res_sender, - NonZeroUsize::new(1).unwrap(), - bootstrapped_view, - buffered_view_num_count as u64, - maximise_txns_count_timeout_duration, - base_fee, + node_count, + maximize_txns_count_timeout_duration, + instance_state + .chain_config + .base_fee + .as_u64() + .context("the base fee exceeds the maximum amount that a builder can pay (defined by u64::MAX)")?, Arc::new(instance_state), + Duration::from_secs(60), + Arc::new(validated_state), ); // spawn the builder event loop @@ -164,41 +190,25 @@ impl BuilderConfig { max_api_timeout_duration, ); - let proxy_global_api_state = Arc::new(RwLock::new(proxy_global_state)); // start the hotshot api service - run_builder_api_service(hotshot_builder_apis_url.clone(), proxy_global_api_state); - - // create a client for it - // Start Client for the event streaming api - tracing::info!( - "Builder client connecting to hotshot events API at {}", - hotshot_events_api_url.to_string() - ); - let client = Client::::new(hotshot_events_api_url.clone()); - - assert!(client.connect(None).await); - - tracing::info!("Builder client connected to the hotshot events api"); - - // client subscrive to hotshot events - let subscribed_events = client - .socket("hotshot-events/events") - .subscribe::>() - .await - .unwrap(); - - tracing::info!("Builder client subscribed to hotshot events"); + run_builder_api_service(hotshot_builder_apis_url.clone(), proxy_global_state); // spawn the builder service + let events_url = hotshot_events_api_url.clone(); + tracing::info!("Running permissionless builder against hotshot events API at {events_url}",); async_spawn(async move { - run_non_permissioned_standalone_builder_service( - tx_sender, + let res = run_non_permissioned_standalone_builder_service( da_sender, qc_sender, decide_sender, - subscribed_events, + tx_sender, + events_url, ) .await; + tracing::error!(?res, "builder service exited"); + if res.is_err() { + panic!("Builder should restart."); + } }); tracing::info!("Builder init finished"); @@ -234,12 +244,10 @@ mod test { events::{Error as EventStreamApiError, Options as EventStreamingApiOptions}, events_source::{BuilderEvent, EventConsumer, EventsStreamer}, }; - use hotshot_types::{ - constants::{Version01, STATIC_VER_0_1}, - traits::{ - block_contents::{BlockPayload, GENESIS_VID_NUM_STORAGE_NODES}, - node_implementation::NodeType, - }, + use hotshot_types::constants::Base; + use hotshot_types::traits::{ + block_contents::{BlockPayload, GENESIS_VID_NUM_STORAGE_NODES}, + node_implementation::NodeType, }; use hotshot_types::{signature_key::BLSPubKey, traits::signature_key::SignatureKey}; use sequencer::{ @@ -248,8 +256,7 @@ mod test { PersistenceOptions, }, state::FeeAccount, - transaction::Transaction, - Payload, + NamespaceId, Payload, Transaction, }; use std::time::Duration; use surf_disco::Client; @@ -281,10 +288,8 @@ mod test { let num_non_staking_nodes = hotshot_config.config.num_nodes_without_stake; // non-staking node handle - let hotshot_context_handle = handles - [NonPermissionedBuilderTestConfig::SUBSCRIBED_DA_NODE_ID] - .0 - .clone(); + let hotshot_context_handle = + &handles[NonPermissionedBuilderTestConfig::SUBSCRIBED_DA_NODE_ID].0; // hotshot event streaming api url let hotshot_events_streaming_api_url = HotShotTestConfig::hotshot_event_streaming_api_url(); @@ -294,11 +299,11 @@ mod test { hotshot_events_streaming_api_url.clone(), known_nodes_with_stake, num_non_staking_nodes, - hotshot_context_handle, + Arc::clone(hotshot_context_handle), ); // builder api url - let hotshot_builder_api_url = hotshot_config.config.builder_url.clone(); + let hotshot_builder_api_url = hotshot_config.config.builder_urls[0].clone(); let builder_config = NonPermissionedBuilderTestConfig::init_non_permissioned_builder( &hotshot_config, @@ -310,7 +315,7 @@ mod test { let builder_pub_key = builder_config.fee_account; // Start a builder api client - let builder_client = Client::::new( + let builder_client = Client::::new( hotshot_builder_api_url.clone(), ); assert!(builder_client.connect(Some(Duration::from_secs(60))).await); @@ -333,10 +338,12 @@ mod test { // sleep and wait for builder service to startup async_sleep(Duration::from_millis(3000)).await; + let test_view_num = 0; + // test getting available blocks let available_block_info = match builder_client .get::>>(&format!( - "block_info/availableblocks/{parent_commitment}/{hotshot_client_pub_key}/{encoded_signature}" + "block_info/availableblocks/{parent_commitment}/{test_view_num}/{hotshot_client_pub_key}/{encoded_signature}" )) .send() .await @@ -363,7 +370,7 @@ mod test { // Test claiming blocks let _available_block_data = match builder_client .get::>(&format!( - "block_info/claimblock/{builder_commitment}/{hotshot_client_pub_key}/{encoded_signature}" + "block_info/claimblock/{builder_commitment}/{test_view_num}/{hotshot_client_pub_key}/{encoded_signature}" )) .send() .await @@ -380,7 +387,7 @@ mod test { // Test claiming block header input let _available_block_header = match builder_client .get::>(&format!( - "block_info/claimheaderinput/{builder_commitment}/{hotshot_client_pub_key}/{encoded_signature}" + "block_info/claimheaderinput/{builder_commitment}/{test_view_num}/{hotshot_client_pub_key}/{encoded_signature}" )) .send() .await @@ -409,7 +416,7 @@ mod test { } } - let txn = Transaction::new(Default::default(), vec![1, 2, 3]); + let txn = Transaction::new(NamespaceId::from(1), vec![1, 2, 3]); match builder_client .post::<()>("txn_submit/submit") .body_json(&txn) diff --git a/builder/src/permissioned.rs b/builder/src/permissioned.rs index 0f40b4371..75e751270 100644 --- a/builder/src/permissioned.rs +++ b/builder/src/permissioned.rs @@ -12,8 +12,8 @@ use hotshot::{ traits::{ election::static_committee::GeneralStaticCommittee, implementations::{ - derive_libp2p_peer_id, CombinedNetworks, KeyPair, Libp2pNetwork, - NetworkingMetricsValue, PushCdnNetwork, WebServerNetwork, WrappedSignatureKey, + derive_libp2p_peer_id, CdnMetricsValue, CombinedNetworks, KeyPair, Libp2pNetwork, + PushCdnNetwork, Topic, WrappedSignatureKey, }, }, types::{SignatureKey, SystemContextHandle}, @@ -28,7 +28,7 @@ use hotshot_types::{ event::Event, light_client::StateKeyPair, signature_key::{BLSPrivKey, BLSPubKey}, - traits::{election::Membership, metrics::Metrics}, + traits::{election::Membership, metrics::Metrics, EncodeBytes}, utils::BuilderCommitment, HotShotConfig, PeerConfig, ValidatorConfig, }; @@ -60,25 +60,27 @@ use hotshot_builder_core::{ service::{ run_non_permissioned_standalone_builder_service, run_permissioned_standalone_builder_service, GlobalState, ProxyGlobalState, + ReceivedTransaction, }, }; use hotshot_state_prover; -use jf_primitives::{ - merkle_tree::{namespaced_merkle_tree::NamespacedMerkleTreeScheme, MerkleTreeScheme}, - signatures::bls_over_bn254::VerKey, -}; -use sequencer::state_signature::StakeTableCommitmentType; -use sequencer::{catchup::mock::MockStateCatchup, eth_signature_key::EthKeyPair, ChainConfig}; +use jf_merkle_tree::{namespaced_merkle_tree::NamespacedMerkleTreeScheme, MerkleTreeScheme}; +use jf_signature::bls_over_bn254::VerKey; use sequencer::{ - catchup::StatePeers, + catchup::{mock::MockStateCatchup, StatePeers}, context::{Consensus, SequencerContext}, + eth_signature_key::EthKeyPair, + genesis::L1Finalized, l1_client::L1Client, network, + network::libp2p::split_off_peer_id, persistence::SequencerPersistence, state::FeeAccount, state::ValidatedState, + state_signature::StakeTableCommitmentType, state_signature::{static_stake_table_commitment, StateSigner}, - BuilderParams, L1Params, NetworkParams, Node, NodeState, Payload, PrivKey, PubKey, SeqTypes, + ChainConfig, Genesis, L1Params, NetworkParams, Node, NodeState, Payload, PrivKey, PubKey, + SeqTypes, }; use std::{alloc::System, any, fmt::Debug, mem}; use std::{marker::PhantomData, net::IpAddr}; @@ -87,7 +89,6 @@ use tide_disco::{app, method::ReadState, App, Url}; use vbs::version::StaticVersionType; use hotshot_types::{ - constants::{Version01, STATIC_VER_0_1}, data::{fake_commitment, Leaf, ViewNumber}, traits::{ block_contents::{vid_commitment, GENESIS_VID_NUM_STORAGE_NODES}, @@ -109,7 +110,7 @@ pub struct BuilderContext< Ver: StaticVersionType + 'static, > { /// The consensus handle - pub hotshot_handle: Consensus, + pub hotshot_handle: Arc>, /// Index of this sequencer node pub node_index: u64, @@ -129,21 +130,21 @@ pub struct BuilderContext< #[allow(clippy::too_many_arguments)] pub async fn init_node( + genesis: Genesis, network_params: NetworkParams, metrics: &dyn Metrics, - builder_params: BuilderParams, l1_params: L1Params, hotshot_builder_api_url: Url, eth_key_pair: EthKeyPair, bootstrapped_view: ViewNumber, - channel_capacity: NonZeroUsize, + tx_channel_capacity: NonZeroUsize, + event_channel_capacity: NonZeroUsize, bind_version: Ver, persistence: P, max_api_timeout_duration: Duration, buffered_view_num_count: usize, is_da: bool, - maximise_txns_count_timeout_duration: Duration, - base_fee: u64, + maximize_txns_count_timeout_duration: Duration, ) -> anyhow::Result> { // Orchestrator client let validator_args = ValidatorArgs { @@ -166,19 +167,36 @@ pub async fn init_node::SignatureKey>(&my_config.private_key) .with_context(|| "Failed to derive Libp2p peer ID")?; - let config = NetworkConfig::get_complete_config( + let mut config = NetworkConfig::get_complete_config( &orchestrator_client, - None, my_config.clone(), // Register in our Libp2p advertise address and public key so other nodes // can contact us on startup Some(network_params.libp2p_advertise_address), Some(libp2p_public_key), - false, ) .await? .0; + // If the `Libp2p` bootstrap nodes were supplied via the command line, override those + // present in the config file. + if let Some(bootstrap_nodes) = network_params.libp2p_bootstrap_nodes { + if let Some(libp2p_config) = config.libp2p_config.as_mut() { + // If the libp2p configuration is present, we can override the bootstrap nodes. + + // Split off the peer ID from the addresses + libp2p_config.bootstrap_nodes = bootstrap_nodes + .into_iter() + .map(split_off_peer_id) + .collect::, _>>() + .with_context(|| "Failed to parse peer ID from bootstrap node")?; + } else { + // If not, don't try launching with them. Eventually we may want to + // provide a default configuration here instead. + tracing::warn!("No libp2p configuration found, ignoring bootstrap nodes"); + } + } + tracing::info!( node_id = config.node_index, stake_table = ?config.config.known_nodes_with_stake, @@ -190,11 +208,12 @@ pub async fn init_node Some(b), + Some(L1Finalized::Number { number }) => { + Some(l1_client.wait_for_finalized_block(number).await) + } + None => None, + }; - let instance_state = NodeState::new( - ChainConfig::default(), + let instance_state = NodeState { + chain_config: genesis.chain_config, l1_client, - Arc::new(StatePeers::::from_urls(network_params.state_peers)), - ); + genesis_header: genesis.header, + genesis_state: genesis_state.clone(), + l1_genesis, + peers: Arc::new(StatePeers::::from_urls( + network_params.state_peers, + network_params.catchup_backoff, + )), + node_id: node_index, + upgrades: Default::default(), + current_version: Ver::VERSION, + }; let stake_table_commit = static_stake_table_commitment(&config.config.known_nodes_with_stake, STAKE_TABLE_CAPACITY); @@ -276,18 +303,19 @@ pub async fn init_node, + hotshot_handle: Arc>, state_signer: StateSigner, node_index: u64, eth_key_pair: EthKeyPair, bootstrapped_view: ViewNumber, - channel_capacity: NonZeroUsize, + tx_channel_capacity: NonZeroUsize, + event_channel_capacity: NonZeroUsize, instance_state: NodeState, + validated_state: ValidatedState, hotshot_builder_api_url: Url, max_api_timeout_duration: Duration, buffered_view_num_count: usize, - maximise_txns_count_timeout_duration: Duration, - base_fee: u64, + maximize_txns_count_timeout_duration: Duration, ) -> anyhow::Result { // tx channel - let (tx_sender, tx_receiver) = broadcast::>(channel_capacity.get()); + let (mut tx_sender, tx_receiver) = + broadcast::>>(tx_channel_capacity.get()); + tx_sender.set_overflow(true); // da channel - let (da_sender, da_receiver) = broadcast::>(channel_capacity.get()); + let (da_sender, da_receiver) = + broadcast::>(event_channel_capacity.get()); // qc channel - let (qc_sender, qc_receiver) = broadcast::>(channel_capacity.get()); + let (qc_sender, qc_receiver) = + broadcast::>(event_channel_capacity.get()); // decide channel let (decide_sender, decide_receiver) = - broadcast::>(channel_capacity.get()); + broadcast::>(event_channel_capacity.get()); // builder api request channel - let (req_sender, req_receiver) = broadcast::>(channel_capacity.get()); + let (req_sender, req_receiver) = + broadcast::>(event_channel_capacity.get()); - // builder api response channel - let (res_sender, res_receiver) = unbounded(); let (genesis_payload, genesis_ns_table) = - Payload::from_transactions([], Arc::new(instance_state.clone())) + Payload::from_transactions([], &validated_state, &instance_state) + .await .expect("genesis payload construction failed"); + let builder_commitment = genesis_payload.builder_commitment(&genesis_ns_table); + let vid_commitment = { // TODO we should not need to collect payload bytes just to compute vid_commitment - let payload_bytes = genesis_payload - .encode() - .expect("unable to encode genesis payload"); + let payload_bytes = genesis_payload.encode(); vid_commitment(&payload_bytes, GENESIS_VID_NUM_STORAGE_NODES) }; // create the global state let global_state: GlobalState = GlobalState::::new( req_sender, - res_receiver, tx_sender.clone(), vid_commitment, bootstrapped_view, @@ -437,22 +471,26 @@ impl::new( + let builder_client = Client::::new( hotshot_builder_api_url.clone(), ); assert!(builder_client.connect(Some(Duration::from_secs(60))).await); @@ -605,15 +641,16 @@ mod test { ) .expect("Claim block signing failed"); + let test_view_num = 0; // test getting available blocks tracing::info!( - "block_info/availableblocks/{parent_commitment}/{hotshot_client_pub_key}/{encoded_signature}" + "block_info/availableblocks/{parent_commitment}/{test_view_num}/{hotshot_client_pub_key}/{encoded_signature}" ); // sleep and wait for builder service to startup async_sleep(Duration::from_millis(3000)).await; let available_block_info = match builder_client .get::>>(&format!( - "block_info/availableblocks/{parent_commitment}/{hotshot_client_pub_key}/{encoded_signature}" + "block_info/availableblocks/{parent_commitment}/{test_view_num}/{hotshot_client_pub_key}/{encoded_signature}" )) .send() .await @@ -640,7 +677,7 @@ mod test { // Test claiming blocks let _available_block_data = match builder_client .get::>(&format!( - "block_info/claimblock/{builder_commitment}/{hotshot_client_pub_key}/{encoded_signature}" + "block_info/claimblock/{builder_commitment}/{test_view_num}/{hotshot_client_pub_key}/{encoded_signature}" )) .send() .await @@ -657,7 +694,7 @@ mod test { // Test claiming block header input let _available_block_header = match builder_client .get::>(&format!( - "block_info/claimheaderinput/{builder_commitment}/{hotshot_client_pub_key}/{encoded_signature}" + "block_info/claimheaderinput/{builder_commitment}/{test_view_num}/{hotshot_client_pub_key}/{encoded_signature}" )) .send() .await @@ -686,7 +723,7 @@ mod test { } } - let txn = Transaction::new(Default::default(), vec![1, 2, 3]); + let txn = Transaction::new(NamespaceId::from(1), vec![1, 2, 3]); match builder_client .post::<()>("txn_submit/submit") .body_json(&txn) diff --git a/contract-bindings/artifacts/LightClientMock_bytecode.json b/contract-bindings/artifacts/LightClientMock_bytecode.json index ad7426e41..2043850b2 100644 --- a/contract-bindings/artifacts/LightClientMock_bytecode.json +++ b/contract-bindings/artifacts/LightClientMock_bytecode.json @@ -1 +1 @@ -"0x60a0604052306080523480156200001557600080fd5b50604051620063ca380380620063ca83398101604081905262000038916200041b565b6200004262000056565b6200004e82826200010a565b5050620004ee565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000a75760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620001075780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b81516001600160401b03161515806200012f575060208201516001600160401b031615155b806200013d57506080820151155b806200014b575060a0820151155b8062000159575060c0820151155b8062000167575060e0820151155b8062000177575063ffffffff8116155b1562000196576040516350dd03f760e11b815260040160405180910390fd5b81600560008060049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e0820151816006015590505081600560008060089054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e08201518160060155905050600080600c6101000a8154816001600160401b0302191690836001600160401b03160217905550806000806101000a81548163ffffffff021916908363ffffffff16021790555060006200038483620003a160201b60201c565b600181905560e09093015160028190556003939093555050600455565b60808082015160a083015160c0840151604080516020810194909452830191909152606082015260009101604051602081830303815290604052805190602001209050919050565b80516001600160401b03811681146200040157600080fd5b919050565b805163ffffffff811681146200040157600080fd5b6000808284036101208112156200043157600080fd5b610100808212156200044257600080fd5b60405191508082016001600160401b03811183821017156200047457634e487b7160e01b600052604160045260246000fd5b6040526200048285620003e9565b82526200049260208601620003e9565b602083015260408501516040830152606085015160608301526080850151608083015260a085015160a083015260c085015160c083015260e085015160e0830152819350620004e381860162000406565b925050509250929050565b608051615eb262000518600039600081816114d2015281816114fb01526116e90152615eb26000f3fe6080604052600436106101965760003560e01c8063715018a6116100e1578063a244d5961161008a578063bd32519a11610064578063bd32519a146106bf578063ca6fe85514610701578063f068205414610717578063f2fde38b1461074957600080fd5b8063a244d596146105e6578063aa92273214610606578063ad3cb1cc1461066957600080fd5b80637f17baad116100bb5780637f17baad146104df57806382d07ff3146105945780638da5cb5b146105a957600080fd5b8063715018a61461046a578063766718081461047f57806376b6b7cb146104c957600080fd5b8063409939b71161014357806352d1902d1161011d57806352d1902d1461042a578063628277331461043f57806369cc6a041461045557600080fd5b8063409939b7146103735780634847ae5d146103935780634f1ef2861461041757600080fd5b8063313df7b111610174578063313df7b1146102b1578063382b215a146102e95780633949d1e91461030d57600080fd5b8063013fa5fc1461019b5780630d8e6e2c146101bd578063202a0adb146101ef575b600080fd5b3480156101a757600080fd5b506101bb6101b6366004615583565b610769565b005b3480156101c957600080fd5b506040805160018152600060208201819052918101919091526060015b60405180910390f35b3480156101fb57600080fd5b506101bb61020a3660046156c5565b6000805463ffffffff600160401b9182900416825260056020818152604093849020855181549287015167ffffffffffffffff9081169095027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909316941693909317178255918301516001820155606083015160028201556080830151600382015560a0830151600482015560c08301519181019190915560e090910151600690910155565b3480156102bd57600080fd5b506006546102d1906001600160a01b031681565b6040516001600160a01b0390911681526020016101e6565b3480156102f557600080fd5b506102ff60035481565b6040519081526020016101e6565b34801561031957600080fd5b506101bb6103283660046156e2565b6000805467ffffffffffffffff9092166c01000000000000000000000000027fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff909216919091179055565b34801561037f57600080fd5b506101bb61038e36600461574c565b610881565b34801561039f57600080fd5b506103a8610bb9565b6040516101e6919060006101008201905067ffffffffffffffff8084511683528060208501511660208401525060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6101bb610425366004615924565b610ca1565b34801561043657600080fd5b506102ff610cc0565b34801561044b57600080fd5b506102ff60025481565b34801561046157600080fd5b506101bb610cef565b34801561047657600080fd5b506101bb610da1565b34801561048b57600080fd5b506000546104b0906c01000000000000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101e6565b3480156104d557600080fd5b506102ff60015481565b3480156104eb57600080fd5b5061054d6104fa3660046159de565b6005602081905260009182526040909120805460018201546002830154600384015460048501549585015460069095015467ffffffffffffffff80861697600160401b9096041695939492939192919088565b6040805167ffffffffffffffff998a168152989097166020890152958701949094526060860192909252608085015260a084015260c083015260e0820152610100016101e6565b3480156105a057600080fd5b506103a8610db3565b3480156105b557600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166102d1565b3480156105f257600080fd5b506101bb6106013660046159f9565b610e98565b34801561061257600080fd5b506102ff6106213660046156c5565b60808082015160a083015160c0840151604080516020810194909452830191909152606082015260009101604051602081830303815290604052805190602001209050919050565b34801561067557600080fd5b506106b26040518060400160405280600581526020017f352e302e3000000000000000000000000000000000000000000000000000000081525081565b6040516101e69190615a64565b3480156106cb57600080fd5b506006546106f19074010000000000000000000000000000000000000000900460ff1681565b60405190151581526020016101e6565b34801561070d57600080fd5b506102ff60045481565b34801561072357600080fd5b506000546107349063ffffffff1681565b60405163ffffffff90911681526020016101e6565b34801561075557600080fd5b506101bb610764366004615583565b611024565b61077161107b565b6001600160a01b0381166107b1576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546001600160a01b03908116908216036107f9576040517fa863aec900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffff000000000000000000000000000000000000000000166001600160a01b0383811691909117740100000000000000000000000000000000000000001791829055604051911681527f8017bb887fdf8fca4314a9d40f6e73b3b81002d67e5cfa85d88173af6aa46072906020015b60405180910390a150565b60065474010000000000000000000000000000000000000000900460ff1680156108b657506006546001600160a01b03163314155b1561092f576006546001600160a01b03166108fd576040517f25cda3ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa3a6478000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610937610db3565b51825167ffffffffffffffff91821691161115806109785750610958610db3565b6020015167ffffffffffffffff16826020015167ffffffffffffffff1611155b156109af576040517f051c46ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080546109de9063ffffffff8116906c01000000000000000000000000900467ffffffffffffffff16615aad565b6000805463ffffffff600160401b918290041682526005602052604090912054919250900467ffffffffffffffff90811690821614801581610a3757508167ffffffffffffffff16846020015167ffffffffffffffff16115b15610a7f576040517f1b2335f800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff831660048201526024015b60405180910390fd5b610a8c84604001516110ef565b610a9984606001516110ef565b610aa684608001516110ef565b610ab38460a001516110ef565b610ac08460c001516110ef565b8015610ace57610ace61115f565b610ad884846112bc565b6000805463ffffffff600160401b9182900416825260056020818152604093849020885181548a84015167ffffffffffffffff9081169687027fffffffffffffffffffffffffffffffff000000000000000000000000000000009092169216918217178255898601516001830181905560608b0151600284015560808b0151600384015560a08b0151600484015560c08b01519483019490945560e08a015160069092019190915593519182529192917fa04a773924505a418564363725f56832f5772e6b8d0dbd6efce724dfe803dae6910160405180910390a350505050565b610c15604051806101000160405280600067ffffffffffffffff168152602001600067ffffffffffffffff1681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5060008054640100000000900463ffffffff16815260056020818152604092839020835161010081018552815467ffffffffffffffff8082168352600160401b90910416928101929092526001810154938201939093526002830154606082015260038301546080820152600483015460a08201529082015460c082015260069091015460e082015290565b610ca96114c7565b610cb282611597565b610cbc82826115d8565b5050565b6000610cca6116de565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b610cf761107b565b60065474010000000000000000000000000000000000000000900460ff1615610d6d57600680547fffffffffffffffffffffff0000000000000000000000000000000000000000001690556040517f9a5f57de856dd668c54dd95e5c55df93432171cbca49a8776d5620ea59c0245090600090a1565b6040517fa863aec900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b610da961107b565b610d9f6000611740565b610e0f604051806101000160405280600067ffffffffffffffff168152602001600067ffffffffffffffff1681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b506000805463ffffffff600160401b9182900416825260056020818152604093849020845161010081018652815467ffffffffffffffff8082168352959004909416918401919091526001810154938301939093526002830154606083015260038301546080830152600483015460a083015282015460c082015260069091015460e082015290565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff16600081158015610ede5750825b905060008267ffffffffffffffff166001148015610efb5750303b155b905081158015610f09575080155b15610f40576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610f8657845468ff00000000000000001916600160401b1785555b610f8f866117c9565b610f976117da565b600080547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff16600160401b179055610fcf88886117e2565b831561101a57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b61102c61107b565b6001600160a01b03811661106f576040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260006004820152602401610a76565b61107881611740565b50565b336110ad7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610d9f576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610a76565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001811080610cbc5760405162461bcd60e51b815260206004820152601b60248201527f426e3235343a20696e76616c6964207363616c6172206669656c6400000000006044820152606401610a76565b60008054600160401b80820463ffffffff16808452600560208181526040808720815161010081018352815467ffffffffffffffff808216835297900487168185015260018083015482850152600280840154606080850191909152600380860154608080870182905260048089015460a08901819052898d015460c08a018190526006909a01805460e0909a01999099528a51808d0194909452838b015282850198909852885180830390940184520190965280519087012085548355948590558354905595895293909252915490559390929091600c916112549185916c01000000000000000000000000900416615ad1565b82546101009290920a67ffffffffffffffff8181021990931691831602179091556000546040516c0100000000000000000000000090910490911681527fdb3558259e039d7e50e816b9dcce30fb114d8a9c86eca5ab14b60194d6945d3f9150602001610876565b60006112c6611ad2565b60408051600880825261012082019092529192506000919060208201610100803683370190505090506002548160008151811061130557611305615af2565b602002602001018181525050836000015167ffffffffffffffff168160018151811061133357611333615af2565b602002602001018181525050836020015167ffffffffffffffff168160028151811061136157611361615af2565b60200260200101818152505083604001518160038151811061138557611385615af2565b6020026020010181815250508360600151816004815181106113a9576113a9615af2565b60209081029190910181019190915260008054600160401b900463ffffffff168152600591829052604090206003015482519091839181106113ed576113ed615af2565b60209081029190910181019190915260008054600160401b900463ffffffff1681526005909152604090206004015481518290600690811061143157611431615af2565b60209081029190910181019190915260008054600160401b900463ffffffff16815260059182905260409020015481518290600790811061147457611474615af2565b60200260200101818152505061148b8282856120b3565b6114c1576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148061156057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166115547f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b031614155b15610d9f576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61159f61107b565b6040516001600160a01b03821681527ff78721226efe9a1bb678189a16d1554928b9f2192e2cb93eeda83b79fa40007d90602001610876565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611632575060408051601f3d908101601f1916820190925261162f91810190615b08565b60015b611673576040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610a76565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146116cf576040517faa1d49a400000000000000000000000000000000000000000000000000000000815260048101829052602401610a76565b6116d9838361219e565b505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610d9f576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6117d16121f4565b61107881612256565b610d9f6121f4565b815167ffffffffffffffff161515806118085750602082015167ffffffffffffffff1615155b8061181557506080820151155b80611822575060a0820151155b8061182f575060c0820151155b8061183c575060e0820151155b8061184b575063ffffffff8116155b15611882576040517fa1ba07ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600560008060049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160000160086101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e0820151816006015590505081600560008060089054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160000160086101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e08201518160060155905050600080600c6101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550806000806101000a81548163ffffffff021916908363ffffffff1602179055506000611ab58360808082015160a083015160c0840151604080516020810194909452830191909152606082015260009101604051602081830303815290604052805190602001209050919050565b600181905560e09093015160028190556003939093555050600455565b611ada61525f565b620100008152600860208201527f013d1d4b425179258b577860397955cbfa0816e32b1c25a1fd734c91b951ee816040820151527f16b88dc7439a6d841e1a1103f5a3d2d2440137f18d02763503bac7b45dcb983b6020604083015101527f0c3c864f195f59119927f53857f1de8bf575941748b735351fd31373c7875c2d6060820151527f169ba15107f2eff9b9341bf30742a838d27dbd69e88b2353dca8592f15f1111c6020606083015101527f11d4ceb15961d10b6156ae3d09bb78b4df45fb8543060884e7d400755beb4ac86080820151527f0326ff06391ed5d26ec1bc080b8d46014ee22c0c68ed022f1620c4d9d38437d36020608083015101527f23610cb43e21033c368a93622dd405b905a0eb344c98b9d7cf08b0c5ebf7c83960a0820151527e1379342a4d77d4708743aff01ff27aa11917478fdc8e2b7d463081735772ea602060a083015101527f194daf85d9eed9937b28e2a680fcc5a76922c15cd31dc4f600e14939b8200ce760c0820151527f25280b124624911c7f87b4c2d87f59c6c07e3eeeb10d614da216f6219ffe50b6602060c083015101527f04882ef39899ea38c9677a48b8f8cc6a67284e17ff940289faaa359eec9b33a660e0820151527f1bae9f36e6190738c711501be53f299bf61348e61e2ef9d577760e64f629368d602060e083015101527f2d810d30120cb93e1a254b89ed0ae86c761f49b4f129459cd534f9551851350f610100820151527f0b25394da5a1d435daccc2eadd039e2c2709f5f42fabd9afba815ed62d6af36b602061010083015101527f1c2ce7be570bea9e43f3d3d7cbca84bdb4fc89b53ae657531de72670a6105e08610120820151527f1a0255ec8c7c8769335bc9dc6b222ac6a04e766d08b45c8cc573592c05bc9c14602061012083015101527f1c164159136b8f5b4773e133f483a8a192ab15d6d3ee012f171b3d02fd4506e7610140820151527f272eb7d633cedb68ce0113f4420ab5610b81b8ba1ab9348db15761d40e8df5ba602061014083015101527f0e446639aa6caf25e93ef747084e39b8ea90abf23bb48c28fd5f9ba7ba655022610160820151527f033e195a9ea3a9ce40b72b673afb45444ca1b15f0543f44d10f5c640a780676f602061016083015101527f0e8db2b2893df23d3c55f0b3a3b54ab2e2ed775b31c4c90e472eb31582582df2610180820151527f0f4c69895451af15052aa81a03eba9752c9e7891dd08e264e0bd593d0357858e602061018083015101527f2bc091fefe2416ec83c6fba8fb82fb48cb9ca00182ec792c212bf5532958c8f76101a0820151527f1f4dcf4dd484e3ab1ae487603d2245397ac78fe6ca7584645eba3926d383902b60206101a083015101527f2daf78c5a2829a9418081dd7e8743a683ed6817996f75f10009d99220793ec736101c0820151527f19eb12a7827c0ddf6383fe806c3953bd06b08aae7bf2a01f55c986a84f50cc2860206101c083015101527f015691308846e68ea856a2cb24c9903f0c8605dea190829180ff6bdd1c6508036101e0820151527f1ffd789b155b8acb13e0f6a48b50f7aa8092540888d009141057d4569091582460206101e083015101527f0545ac7aa66dcf3719988438c806fc624de57ab43f8580392f88c86c1378ce4a610200820151527f16b7f250842ecf4e3690706a1e152d7a57f70f556f92076da785fdd363c19fcf602061020083015101527f20cb7ff35a83a7dc314036e470f14c30fb0e98d35d663b243b222caa6fc7db44610220820151527f149f415744707468bdaa4e8545201ab40d1931a7d31f23768fa7c65574ee3eab602061022083015101527f0a25c1b7573906dc4e193b4ea82fd1fe7ccebc4d925dad26f0ff09c84c9f1a75610240820151527f0a521ff30c8f3666798f847c5d4c379658fba10156e7a9499f2713fae9bf2be1602061024083015101527f03db6510c3f13629fded9a5a2d41654bbce4ef6d024cad53100051d4a3f3ebc9610260820151527f08e80a5c8e4c9b9f26f3003cc59403a18d3136afd030868d25cc8b807e2ab3706020610260830151015290565b60006120be8261225e565b6120e1836000815181106120d4576120d4615af2565b60200260200101516110ef565b6120f7836001815181106120d4576120d4615af2565b61210d836002815181106120d4576120d4615af2565b612123836003815181106120d4576120d4615af2565b612139836004815181106120d4576120d4615af2565b61214f836005815181106120d4576120d4615af2565b612165836006815181106120d4576120d4615af2565b61217b836007815181106120d4576120d4615af2565b6000612188858585612396565b905061219381612517565b9150505b9392505050565b6121a7826129df565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156121ec576116d98282612a87565b610cbc612aff565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16610d9f576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61102c6121f4565b805161226990612b37565b6122768160200151612b37565b6122838160400151612b37565b6122908160600151612b37565b61229d8160800151612b37565b6122aa8160a00151612b37565b6122b78160c00151612b37565b6122c48160e00151612b37565b6122d2816101000151612b37565b6122e0816101200151612b37565b6122ee816101400151612b37565b6122fc816101600151612b37565b61230a816101800151612b37565b612318816101a001516110ef565b612326816101c001516110ef565b612334816101e001516110ef565b6123428161020001516110ef565b6123508161022001516110ef565b61235e8161024001516110ef565b61236c8161026001516110ef565b61237a8161028001516110ef565b612388816102a001516110ef565b611078816102c001516110ef565b61239e6154dd565b83602001518351146123dc576040517f41f53b1200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006123e9858585612be1565b905060006123fa8660000151612f12565b9050600061240d828460a00151886132f6565b60408051601e8082526103e0820190925291925060009190602082016103c080368337505060408051601e8082526103e082019092529293506000929150602082015b604080518082019091526000808252602082015281526020019060019003908161245057905050905060006124898a858a898787613356565b60a08701516060870151919250907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600081838509604080516101008101825260e09c8d0151815260208101969096528501525050506060810191909152608081019290925260a082015261016086015160c082015261018090950151928501929092525091949350505050565b6040805180820182526000808252602080830182905283518085018552828152908101829052835160028082526060820190955291937f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001939285918160200160208202803683375050604080516002808252606082019092529293506000929150602082015b604080518082019091526000808252602082015281526020019060019003908161259d57905050905060006001905080836000815181106125e0576125e0615af2565b6020026020010181815250508760c001518260008151811061260457612604615af2565b602002602001018190525087600001518360018151811061262757612627615af2565b6020026020010181815250508760e001518260018151811061264b5761264b615af2565b6020026020010181905250612660828461338b565b608089015151909550606093508392509050600061267f826002615b21565b61268a906001615b21565b90508067ffffffffffffffff8111156126a5576126a561559e565b6040519080825280602002602001820160405280156126ce578160200160208202803683370190505b5093508067ffffffffffffffff8111156126ea576126ea61559e565b60405190808252806020026020018201604052801561272f57816020015b60408051808201909152600080825260208201528152602001906001900390816127085790505b509250505060008060005b8960800151518110156127d3578960800151818151811061275d5761275d615af2565b602002602001015185838151811061277757612777615af2565b6020026020010181815250508960a00151818151811061279957612799615af2565b60200260200101518483815181106127b3576127b3615af2565b60209081029190910101526127c9600183615b21565b915060010161273a565b5088602001518482815181106127eb576127eb615af2565b6020026020010181815250508860c0015183828151811061280e5761280e615af2565b6020908102919091010152612824600182615b21565b895160408b01519192509060008982840990508087858151811061284a5761284a615af2565b6020026020010181815250505050508860e0015183828151811061287057612870615af2565b6020908102919091010152612886600182615b21565b60608a01519091508781840892505061289e82613485565b8482815181106128b0576128b0615af2565b6020026020010181815250506128e8604080518082018252600080825260209182015281518083019092526001825260029082015290565b8382815181106128fa576128fa615af2565b6020026020010181905250612917612912848661338b565b6134db565b945050505050600060405180608001604052807f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081526020017f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181526020017f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e5581526020017f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe481525090506129d58382846129d061357a565b61364b565b9695505050505050565b806001600160a01b03163b600003612a2e576040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610a76565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6060600080846001600160a01b031684604051612aa49190615b34565b600060405180830381855af49150503d8060008114612adf576040519150601f19603f3d011682016040523d82523d6000602084013e612ae4565b606091505b5091509150612af485838361372f565b925050505b92915050565b3415610d9f576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160208201516000917f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47911590151615612b7157505050565b8251602084015182600384858586098509088382830914838210848410161693505050816116d95760405162461bcd60e51b815260206004820152601760248201527f426e3235343a20696e76616c696420473120706f696e740000000000000000006044820152606401610a76565b612c2960405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805180820190915260608152600060208201527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001612c6a8287876137a4565b81518451612c7790613b85565b612c848660200151613b85565b612c918760400151613b85565b612c9e8860600151613b85565b612cab8960800151613b85565b604051602001612cc096959493929190615b50565b60408051601f198184030181529190528252612cdb82613c48565b50612ce582613c48565b6060840152612cf382613c48565b6080840152815160a0850151612d0890613b85565b604051602001612d19929190615bcf565b60408051601f198184030181529190528252612d3482613c48565b8352815160c0850151612d4690613b85565b612d538660e00151613b85565b612d61876101000151613b85565b612d6f886101200151613b85565b612d7d896101400151613b85565b604051602001612d9296959493929190615b50565b60408051601f198184030181529190528252612dad82613c48565b60a084015281516101a0850151612dc390613cbc565b612dd1866101c00151613cbc565b612ddf876101e00151613cbc565b612ded886102000151613cbc565b612dfb896102200151613cbc565b604051602001612e1096959493929190615bfe565b60408051601f19818403018152919052808352610240850151612e3290613cbc565b612e40866102600151613cbc565b612e4e876102800151613cbc565b612e5c886102a00151613cbc565b612e6a896102c00151613cbc565b604051602001612e7f96959493929190615bfe565b60408051601f198184030181529190528252612e9a82613c48565b60c08401528151610160850151612eb090613b85565b612ebe866101800151613b85565b604051602001612ed093929190615c3a565b60408051601f198184030181529190528252612eeb82613c48565b60e08401528251818180098282820960208601919091526040850152509195945050505050565b612f446040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b816201000003612fd857506040805160a0810182526010815260208101929092527f30641e0e92bebef818268d663bcad6dbcfd6c0149170f6d7d350b1b1fa6c1001908201527eeeb2cb5981ed45649abebde081dcff16c8601de4347e7dd1628ba2daac43b760608201527f0b5d56b77fe704e8e92338c0082f37e091126414c830e4c6922d5ac802d842d4608082015290565b81620200000361306d57506040805160a0810182526011815260208101929092527f30643640b9f82f90e83b698e5ea6179c7c05542e859533b48b9953a2f5360801908201527f1bf82deba7d74902c3708cc6e70e61f30512eca95655210e276e5858ce8f58e560608201527f244cf010c43ca87237d8b00bf9dd50c4c01c7f086bd4e8c920e75251d96f0d22608082015290565b81620400000361310257506040805160a0810182526012815260208101929092527f30644259cd94e7dd5045d7a27013b7fcd21c9e3b7fa75222e7bda49b729b0401908201527f19ddbcaf3a8d46c15c0176fbb5b95e4dc57088ff13f4d1bd84c6bfa57dcdc0e060608201527f036853f083780e87f8d7c71d111119c57dbe118c22d5ad707a82317466c5174c608082015290565b81620800000361319757506040805160a0810182526013815260208101929092527f3064486657634403844b0eac78ca882cfd284341fcb0615a15cfcd17b14d8201908201527f2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd60608201527f06e402c0a314fb67a15cf806664ae1b722dbc0efe66e6c81d98f9924ca535321608082015290565b81621000000361322c57506040805160a0810182526014815260208101929092527f30644b6c9c4a72169e4daa317d25f04512ae15c53b34e8f5acd8e155d0a6c101908201527f26125da10a0ed06327508aba06d1e303ac616632dbed349f53422da95333785760608201527f100c332d2100895fab6473bc2c51bfca521f45cb3baca6260852a8fde26c91f3608082015290565b816020036132bf57506040805160a0810182526005815260208101929092527f2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001908201527f09c532c6306b93d29678200d47c0b2a99c18d51b838eeb1d3eed4c533bb512d060608201527f2724713603bfbd790aeaf3e7df25d8e7ef8f311334905b4d8c99980cf210979d608082015290565b6040517fe2ef09e500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b61331a60405180606001604052806000815260200160008152602001600081525090565b6133248484613e08565b8082526133349085908590613e6e565b6020820152805161334a90859084908690613ef4565b60408201529392505050565b6000806133648587896140bb565b90506133748886898988886141b9565b61337f8187866144d7565b98975050505050505050565b604080518082019091526000808252602082015282518251146133f05760405162461bcd60e51b815260206004820181905260248201527f4d534d206572726f723a206c656e67746820646f6573206e6f74206d617463686044820152606401610a76565b61342e8360008151811061340657613406615af2565b60200260200101518360008151811061342157613421615af2565b6020026020010151614539565b905060015b825181101561347e576134748261346f86848151811061345557613455615af2565b602002602001015186858151811061342157613421615af2565b6145dd565b9150600101613433565b5092915050565b60006134b17f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183615c93565b612af9907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001615cb5565b6040805180820190915260008082526020820152815160208301511590151615613503575090565b6040518060400160405280836000015181526020017f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784602001516135489190615c93565b613572907f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47615cb5565b905292915050565b6135a56040518060800160405280600081526020016000815260200160008152602001600081525090565b60405180608001604052807f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed81526020017f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c281526020017f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa81526020017f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b815250905090565b60008060006040518751815260208801516020820152602087015160408201528651606082015260608701516080820152604087015160a0820152855160c0820152602086015160e08201526020850151610100820152845161012082015260608501516101408201526040850151610160820152602060006101808360085afa9150506000519150806137215760405162461bcd60e51b815260206004820152601c60248201527f426e3235343a2050616972696e6720636865636b206661696c656421000000006044820152606401610a76565b50151590505b949350505050565b6060826137445761373f82614684565b612197565b815115801561375b57506001600160a01b0384163b155b1561379d576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610a76565b5080612197565b825160fe906137df6137b583613cbc565b6040516020016137c791815260200190565b604051602081830303815290604052600060046146c6565b6138196137ef8660000151613cbc565b60405160200161380191815260200190565b604051602081830303815290604052600060086146c6565b6138296137ef8760200151613cbc565b60405160200161383c9493929190615cc8565b60408051601f198184030181529190528085526138596001613cbc565b6138827f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a613cbc565b6138ab7f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb025613cbc565b6138d47f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a613cbc565b6138fd7f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e881613cbc565b60405160200161391296959493929190615bfe565b60408051601f1981840301815291905280855260e084015161393390613b85565b613941856101000151613b85565b61394f866101200151613b85565b61395d876101400151613b85565b61396b886101600151613b85565b613979896101800151613b85565b6139878a6101e00151613b85565b60405160200161399e989796959493929190615d1f565b60408051601f198184030181529190528085526102008401516139c090613b85565b6139ce856102200151613b85565b6139dc866102400151613b85565b6139ea876101a00151613b85565b6139f8886101c00151613b85565b613a06896102600151613b85565b604051602001613a1c9796959493929190615dc4565b60408051601f19818403018152918152818652840151613a3b90613b85565b613a488560600151613b85565b613a558660800151613b85565b613a628760a00151613b85565b613a6f8860c00151613b85565b604051602001613a8496959493929190615b50565b60408051601f198184030181529190528085528251613abb908490600090613aae57613aae615af2565b6020026020010151613cbc565b613ad184600181518110613aae57613aae615af2565b613ae785600281518110613aae57613aae615af2565b613afd86600381518110613aae57613aae615af2565b613b1387600481518110613aae57613aae615af2565b613b2988600581518110613aae57613aae615af2565b613b3f89600681518110613aae57613aae615af2565b613b558a600781518110613aae57613aae615af2565b604051602001613b6d99989796959493929190615e56565b60408051601f19818403018152919052909352505050565b805160208201516060916000911590151615613bbe577f4000000000000000000000000000000000000000000000000000000000000000175b60208301517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760019190911b10613c1257507f80000000000000000000000000000000000000000000000000000000000000005b8251613c1f908217613cbc565b604051602001613c3191815260200190565b604051602081830303815290604052915050919050565b602080820151825180516040518381526000948594939291908101855b83811015613c7f5760208186018101518383015201613c65565b505060209182019020908601819052925060006129d57f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185615c93565b60008190506008817eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff16901b6008827fff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0016901c1790506010817dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff16901b6010827fffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff000016901c1790506020817bffffffff00000000ffffffff00000000ffffffff00000000ffffffff16901b6020827fffffffff00000000ffffffff00000000ffffffff00000000ffffffff0000000016901c17905060408177ffffffffffffffff0000000000000000ffffffffffffffff16901b6040827fffffffffffffffff0000000000000000ffffffffffffffff000000000000000016901c179050608081901b608082901c179050919050565b81516000907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190838015613e5e5784935060005b82811015613e5257838586099450600101613e3c565b50600184039350613e65565b6001830393505b50505092915050565b600082600103613e8057506001612197565b81600003613e9057506000612197565b60408401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190600090828186099050858015613ed257600187039250613ed9565b6001840392505b50613ee3826147ee565b915082828209979650505050505050565b82516000907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190838303613f8757600160005b82811015613f7a57818703613f5b57878181518110613f4857613f48615af2565b6020026020010151945050505050613727565b8380613f6957613f69615c7d565b896060015183099150600101613f27565b5060009350505050613727565b6000806000808a604001519050600080613fa18d886148a6565b905060008767ffffffffffffffff811115613fbe57613fbe61559e565b604051908082528060200260200182016040528015613fe7578160200160208202803683370190505b509050888b850993506001925060005b8881101561402c57602081026020840101519550898d878c030896508987850960208281028401018890529350600101613ff7565b50614036836147ee565b925060005b888110156140a95760208102602084010151955089868609975089848909975060005b8981101561408857808214614080576020810260208401015197508a888a0998505b60010161405e565b506020810260208f010151955089868909975089888c089a5060010161403b565b50505050505050505050949350505050565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019050600083602001519050600084604001519050600060019050606088015160808901516101a08901516102408a0151878889838709858501088609945050506101c08901516102608a0151878889838709858501088609945050506101e08901516102808a0151878889838709858501088609945050506102008901516102a08a01518788898387098585010886099450505061022089015191506102c0890151868782898587080985099350505050875160208901518586868309870385089650508485838309860387089998505050505050505050565b6141c7868686868587614997565b60c085015182517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019190819081908690601490811061420857614208615af2565b60200260200101818152505085600001518460148151811061422c5761422c615af2565b6020026020010181905250828282099050808560158151811061425157614251615af2565b60200260200101818152505085602001518460158151811061427557614275615af2565b6020026020010181905250828282099050808560168151811061429a5761429a615af2565b6020026020010181815250508560400151846016815181106142be576142be615af2565b602002602001018190525082828209905080856017815181106142e3576142e3615af2565b60200260200101818152505085606001518460178151811061430757614307615af2565b6020026020010181905250828282099050808560188151811061432c5761432c615af2565b60200260200101818152505085608001518460188151811061435057614350615af2565b6020026020010181905250828282099050808560198151811061437557614375615af2565b60200260200101818152505088604001518460198151811061439957614399615af2565b60200260200101819052508282820990508085601a815181106143be576143be615af2565b602002602001018181525050886060015184601a815181106143e2576143e2615af2565b60200260200101819052508282820990508085601b8151811061440757614407615af2565b602002602001018181525050886080015184601b8151811061442b5761442b615af2565b60200260200101819052508282820990508085601c8151811061445057614450615af2565b6020026020010181815250508860a0015184601c8151811061447457614474615af2565b60200260200101819052508282820990508760e0015185601d8151811061449d5761449d615af2565b6020026020010181815250508560a0015184601d815181106144c1576144c1615af2565b6020026020010181905250505050505050505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018381039060005b600a8110156145305760206015820102840151602082026101a00186015183848284098608945050506001016144ff565b50509392505050565b6040805180820190915260008082526020820152614555615530565b835181526020808501519082015260408101839052600060608360808460076107d05a03fa9050808061458757600080fd5b50806145d55760405162461bcd60e51b815260206004820152601960248201527f426e3235343a207363616c6172206d756c206661696c656421000000000000006044820152606401610a76565b505092915050565b60408051808201909152600080825260208201526145f961554e565b8351815260208085015181830152835160408301528301516060808301919091526000908360c08460066107d05a03fa9050808061463657600080fd5b50806145d55760405162461bcd60e51b815260206004820152601d60248201527f426e3235343a2067726f7570206164646974696f6e206661696c6564210000006044820152606401610a76565b8051156146945780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060816146d481601f615b21565b10156147225760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610a76565b61472c8284615b21565b8451101561477c5760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610a76565b60608215801561479b57604051915060008252602082016040526147e5565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156147d45780518352602092830192016147bc565b5050858452601f01601f1916604052505b50949350505050565b60008060007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905060405160208152602080820152602060408201528460608201526002820360808201528160a08201526020600060c08360055afa92505060005192508161489f5760405162461bcd60e51b815260206004820152601d60248201527f426e3235343a20706f7720707265636f6d70696c65206661696c6564210000006044820152606401610a76565b5050919050565b606082602001518211156148e6576040517f8c5e11f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606083015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018467ffffffffffffffff8111156149275761492761559e565b604051908082528060200260200182016040528015614950578160200160208202803683370190505b50935084151915613e655760208401856020028101600182526020820191505b8082101561498c57828585099350838252602082019150614970565b505050505092915050565b6000806000806000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190508060208b015160208d01510995508a5193508060a08c015160608d0151099250806101a08a0151840891508060808c015183089150808483099350807f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a84099150806101c08a0151830891508060808c015183089150808483099350807f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb02584099150806101e08a0151830891508060808c015183089150808483099350807f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a84099150806102008a0151830891508060808c015183089150808483099350807f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e88184099150806102208a0151830891508060808c0151830891508084830993508084870895508860a0015188600081518110614b2057614b20615af2565b60200260200101819052508587600081518110614b3f57614b3f615af2565b6020026020010181815250508060608c01518c51099450806102c08a015186099450806102408a015160608d0151099250806101a08a0151840892508060808c015184089250808386099450806102608a015160608d0151099250806101c08a0151840892508060808c015184089250808386099450806102808a015160608d0151099250806101e08a0151840892508060808c015184089250808386099450806102a08a015160608d0151099250806102008a0151840892508060808c0151840892508083860994508b60c0015188600181518110614c2157614c21615af2565b6020908102919091010152614c368582615cb5565b87600181518110614c4957614c49615af2565b602002602001018181525050886101a0015187600281518110614c6e57614c6e615af2565b602002602001018181525050886101c0015187600381518110614c9357614c93615af2565b602002602001018181525050886101e0015187600481518110614cb857614cb8615af2565b60200260200101818152505088610200015187600581518110614cdd57614cdd615af2565b6020026020010181815250508b60e0015188600281518110614d0157614d01615af2565b60200260200101819052508b610100015188600381518110614d2557614d25615af2565b60200260200101819052508b610120015188600481518110614d4957614d49615af2565b60200260200101819052508b610140015188600581518110614d6d57614d6d615af2565b6020026020010181905250806101c08a01516101a08b01510992508287600681518110614d9c57614d9c615af2565b6020026020010181815250508b610160015188600681518110614dc157614dc1615af2565b6020026020010181905250806102008a01516101e08b01510992508287600781518110614df057614df0615af2565b6020026020010181815250508b610180015188600781518110614e1557614e15615af2565b60200260200101819052506101a089015192508083840991508082830991508082840992508287600881518110614e4e57614e4e615af2565b6020026020010181815250508b6101e0015188600881518110614e7357614e73615af2565b60200260200101819052506101c089015192508083840991508082830991508082840992508287600981518110614eac57614eac615af2565b6020026020010181815250508b610200015188600981518110614ed157614ed1615af2565b60200260200101819052506101e089015192508083840991508082830991508082840992508287600a81518110614f0a57614f0a615af2565b6020026020010181815250508b610220015188600a81518110614f2f57614f2f615af2565b602002602001018190525061020089015192508083840991508082830991508082840992508287600b81518110614f6857614f68615af2565b6020026020010181815250508b610240015188600b81518110614f8d57614f8d615af2565b602002602001018190525088610220015181614fa99190615cb5565b87600c81518110614fbc57614fbc615af2565b6020026020010181815250508b6101a0015188600c81518110614fe157614fe1615af2565b6020026020010181905250600187600d8151811061500157615001615af2565b6020026020010181815250508b6101c0015188600d8151811061502657615026615af2565b6020026020010181905250806101c08a01516101a08b0151099250806101e08a015184099250806102008a015184099250806102208a0151840992508287600e8151811061507657615076615af2565b6020026020010181815250508b610260015188600e8151811061509b5761509b615af2565b602090810291909101015289516150b29082615cb5565b87600f815181106150c5576150c5615af2565b6020026020010181815250508860c0015188600f815181106150e9576150e9615af2565b60200260200101819052508060018b510860a08c015190935081908009915080828409925080836020601002890151099150818760108151811061512f5761512f615af2565b6020026020010181815250508860e001518860108151811061515357615153615af2565b602002602001018190525080836020601102890151099150818760118151811061517f5761517f615af2565b602002602001018181525050886101000151886011815181106151a4576151a4615af2565b60200260200101819052508083602060120289015109915081876012815181106151d0576151d0615af2565b602002602001018181525050886101200151886012815181106151f5576151f5615af2565b602002602001018190525080836020601302890151099150818760138151811061522157615221615af2565b6020026020010181815250508861014001518860138151811061524657615246615af2565b6020026020010181905250505050505050505050505050565b6040518061028001604052806000815260200160008152602001615296604051806040016040528060008152602001600081525090565b81526020016152b8604051806040016040528060008152602001600081525090565b81526020016152da604051806040016040528060008152602001600081525090565b81526020016152fc604051806040016040528060008152602001600081525090565b815260200161531e604051806040016040528060008152602001600081525090565b8152602001615340604051806040016040528060008152602001600081525090565b8152602001615362604051806040016040528060008152602001600081525090565b8152602001615384604051806040016040528060008152602001600081525090565b81526020016153a6604051806040016040528060008152602001600081525090565b81526020016153c8604051806040016040528060008152602001600081525090565b81526020016153ea604051806040016040528060008152602001600081525090565b815260200161540c604051806040016040528060008152602001600081525090565b815260200161542e604051806040016040528060008152602001600081525090565b8152602001615450604051806040016040528060008152602001600081525090565b8152602001615472604051806040016040528060008152602001600081525090565b8152602001615494604051806040016040528060008152602001600081525090565b81526020016154b6604051806040016040528060008152602001600081525090565b81526020016154d8604051806040016040528060008152602001600081525090565b905290565b6040518061010001604052806000815260200160008152602001600081526020016000815260200160608152602001606081526020016154b6604051806040016040528060008152602001600081525090565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b80356001600160a01b03811681146132f157600080fd5b60006020828403121561559557600080fd5b6121978261556c565b634e487b7160e01b600052604160045260246000fd5b6040516102e0810167ffffffffffffffff811182821017156155d8576155d861559e565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156156075761560761559e565b604052919050565b803567ffffffffffffffff811681146132f157600080fd5b600061010080838503121561563b57600080fd5b6040519081019067ffffffffffffffff8211818310171561565e5761565e61559e565b8160405280925061566e8461560f565b815261567c6020850161560f565b602082015260408401356040820152606084013560608201526080840135608082015260a084013560a082015260c084013560c082015260e084013560e0820152505092915050565b600061010082840312156156d857600080fd5b6121978383615627565b6000602082840312156156f457600080fd5b6121978261560f565b60006040828403121561570f57600080fd5b6040516040810181811067ffffffffffffffff821117156157325761573261559e565b604052823581526020928301359281019290925250919050565b60008082840361058081121561576157600080fd5b61576b8585615627565b9250610100610480807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00840112156157a257600080fd5b6157aa6155b4565b92506157b8878388016156fd565b83526101406157c9888289016156fd565b60208501526101806157dd89828a016156fd565b60408601526101c06157f18a828b016156fd565b60608701526102006158058b828c016156fd565b60808801526102406158198c828d016156fd565b60a089015261028061582d8d828e016156fd565b60c08a01526102c06158418e828f016156fd565b60e08b01526158548e6103008f016156fd565b898b01526158668e6103408f016156fd565b6101208b015261587a8e6103808f016156fd565b878b015261588c8e6103c08f016156fd565b6101608b01526158a08e6104008f016156fd565b868b01526104408d01356101a08b01526104608d0135858b0152878d01356101e08b01526104a08d0135848b01526104c08d01356102208b01526104e08d0135838b01526105008d01356102608b01526105208d0135828b01526105408d01356102a08b01526105608d0135818b0152505050505050505050809150509250929050565b6000806040838503121561593757600080fd5b6159408361556c565b915060208084013567ffffffffffffffff8082111561595e57600080fd5b818601915086601f83011261597257600080fd5b8135818111156159845761598461559e565b61599684601f19601f840116016155de565b915080825287848285010111156159ac57600080fd5b80848401858401376000848284010152508093505050509250929050565b803563ffffffff811681146132f157600080fd5b6000602082840312156159f057600080fd5b612197826159ca565b60008060006101408486031215615a0f57600080fd5b615a198585615627565b9250615a2861010085016159ca565b9150615a37610120850161556c565b90509250925092565b60005b83811015615a5b578181015183820152602001615a43565b50506000910152565b6020815260008251806020840152615a83816040850160208701615a40565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8181168382160280821691908281146145d5576145d5615a97565b67ffffffffffffffff81811683821601908082111561347e5761347e615a97565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615b1a57600080fd5b5051919050565b80820180821115612af957612af9615a97565b60008251615b46818460208701615a40565b9190910192915050565b600087516020615b638285838d01615a40565b885191840191615b768184848d01615a40565b8851920191615b888184848c01615a40565b8751920191615b9a8184848b01615a40565b8651920191615bac8184848a01615a40565b8551920191615bbe8184848901615a40565b919091019998505050505050505050565b60008351615be1818460208801615a40565b835190830190615bf5818360208801615a40565b01949350505050565b60008751615c10818460208c01615a40565b9190910195865250602085019390935260408401919091526060830152608082015260a001919050565b60008451615c4c818460208901615a40565b845190830190615c60818360208901615a40565b8451910190615c73818360208801615a40565b0195945050505050565b634e487b7160e01b600052601260045260246000fd5b600082615cb057634e487b7160e01b600052601260045260246000fd5b500690565b81810381811115612af957612af9615a97565b60008551615cda818460208a01615a40565b855190830190615cee818360208a01615a40565b8551910190615d01818360208901615a40565b8451910190615d14818360208801615a40565b019695505050505050565b600089516020615d328285838f01615a40565b8a5191840191615d458184848f01615a40565b8a51920191615d578184848e01615a40565b8951920191615d698184848d01615a40565b8851920191615d7b8184848c01615a40565b8751920191615d8d8184848b01615a40565b8651920191615d9f8184848a01615a40565b8551920191615db18184848901615a40565b919091019b9a5050505050505050505050565b600088516020615dd78285838e01615a40565b895191840191615dea8184848e01615a40565b8951920191615dfc8184848d01615a40565b8851920191615e0e8184848c01615a40565b8751920191615e208184848b01615a40565b8651920191615e328184848a01615a40565b8551920191615e448184848901615a40565b919091019a9950505050505050505050565b60008a51615e68818460208f01615a40565b9190910198895250602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000191905056fea164736f6c6343000817000a" +"0x60a0604052306080523480156200001557600080fd5b5060405162006471380380620064718339810160408190526200003891620004ec565b6200004262000056565b6200004e82826200010a565b5050620005bf565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000a75760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620001075780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b81516001600160401b03161515806200012f575060208201516001600160401b031615155b806200013d57506080820151155b806200014b575060a0820151155b8062000159575060c0820151155b8062000167575060e0820151155b8062000177575063ffffffff8116155b1562000196576040516350dd03f760e11b815260040160405180910390fd5b81600560008060049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e0820151816006015590505081600560008060089054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e08201518160060155905050600080600c6101000a8154816001600160401b0302191690836001600160401b03160217905550806000806101000a81548163ffffffff021916908363ffffffff160217905550600062000384836200047260201b60201c565b600181815560e085015160028181556003939093556004556007805480830182556000918252437fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688909101556040805180820182526020808901516001600160401b039081168352929098015197810197885260088054948501815590925290517ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee39290930291820180546001600160401b031916939091169290921790915592517ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee4909301929092555050565b60808082015160a083015160c0840151604080516020810194909452830191909152606082015260009101604051602081830303815290604052805190602001209050919050565b80516001600160401b0381168114620004d257600080fd5b919050565b805163ffffffff81168114620004d257600080fd5b6000808284036101208112156200050257600080fd5b610100808212156200051357600080fd5b60405191508082016001600160401b03811183821017156200054557634e487b7160e01b600052604160045260246000fd5b6040526200055385620004ba565b82526200056360208601620004ba565b602083015260408501516040830152606085015160608301526080850151608083015260a085015160a083015260c085015160c083015260e085015160e0830152819350620005b4818601620004d7565b925050509250929050565b608051615e88620005e9600039600081816116290152818161165201526117be0152615e886000f3fe60806040526004361061017e5760003560e01c8063013fa5fc146101835780630d8e6e2c146101a5578063202a0adb146101d7578063313df7b114610280578063382b215a146102ad5780633919340f146102d15780633949d1e9146102f1578063409939b7146103385780634847ae5d146103585780634f1ef286146103da57806352d1902d146103ed578063530ca78f146104025780635464608514610422578063628277331461043757806369cc6a041461044d5780637053fc5114610462578063715018a614610477578063766718081461048c57806376b6b7cb146104c05780637f17baad146104d657806382d07ff3146105895780638584d23f1461059e5780638da5cb5b146105e2578063a244d596146105f7578063a51e6fea14610617578063aa92273214610637578063ad3cb1cc14610657578063bd32519a14610695578063ca6fe855146106c6578063db13b60a146106dc578063e03033011461071b578063f06820541461073b578063f2fde38b1461076d575b600080fd5b34801561018f57600080fd5b506101a361019e366004615258565b61078d565b005b3480156101b157600080fd5b506040805160018152600060208201819052918101919091526060015b60405180910390f35b3480156101e357600080fd5b506101a36101f23660046153b8565b6000805463ffffffff600160401b918290041682526005602081815260409384902085518154928701516001600160401b039081169095026001600160801b0319909316941693909317178255918301516001820155606083015160028201556080830151600382015560a0830151600482015560c08301519181019190915560e090910151600690910155565b34801561028c57600080fd5b506006546102a0906001600160a01b031681565b6040516101ce91906153d5565b3480156102b957600080fd5b506102c360035481565b6040519081526020016101ce565b3480156102dd57600080fd5b506101a36102ec36600461540c565b61084d565b3480156102fd57600080fd5b506101a361030c3660046154a1565b600080546001600160401b03909216600160601b02600160601b600160a01b0319909216919091179055565b34801561034457600080fd5b506101a36103533660046154ec565b610870565b34801561036457600080fd5b5061036d610b9b565b6040516101ce919060006101008201905060018060401b038084511683528060208501511660208401525060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6101a36103e83660046156a6565b610c2d565b3480156103f957600080fd5b506102c3610c48565b34801561040e57600080fd5b506101a361041d36600461574b565b610c65565b34801561042e57600080fd5b506008546102c3565b34801561044357600080fd5b506102c360025481565b34801561045957600080fd5b506101a3610ce1565b34801561046e57600080fd5b506007546102c3565b34801561048357600080fd5b506101a3610d51565b34801561049857600080fd5b506000546104b390600160601b90046001600160401b031681565b6040516101ce91906157fc565b3480156104cc57600080fd5b506102c360015481565b3480156104e257600080fd5b506105436104f1366004615824565b600560208190526000918252604090912080546001820154600283015460038401546004850154958501546006909501546001600160401b0380861697600160401b9096041695939492939192919088565b604080516001600160401b03998a168152989097166020890152958701949094526060860192909252608085015260a084015260c083015260e0820152610100016101ce565b34801561059557600080fd5b5061036d610d63565b3480156105aa57600080fd5b506105be6105b936600461583f565b610df3565b6040805182516001600160401b0316815260209283015192810192909252016101ce565b3480156105ee57600080fd5b506102a0610f4d565b34801561060357600080fd5b506101a3610612366004615858565b610f68565b34801561062357600080fd5b506102c361063236600461583f565b611093565b34801561064357600080fd5b506102c36106523660046153b8565b6110b4565b34801561066357600080fd5b50610688604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516101ce91906158c3565b3480156106a157600080fd5b506006546106b690600160a01b900460ff1681565b60405190151581526020016101ce565b3480156106d257600080fd5b506102c360045481565b3480156106e857600080fd5b506106fc6106f736600461583f565b6110fc565b604080516001600160401b0390931683526020830191909152016101ce565b34801561072757600080fd5b506106b66107363660046158f6565b611134565b34801561074757600080fd5b506000546107589063ffffffff1681565b60405163ffffffff90911681526020016101ce565b34801561077957600080fd5b506101a3610788366004615258565b611217565b610795611255565b6001600160a01b0381166107bc5760405163e6c4247b60e01b815260040160405180910390fd5b6006546001600160a01b03908116908216036107eb5760405163a863aec960e01b815260040160405180910390fd5b600680546001600160a81b0319166001600160a01b0380841691909117600160a01b17918290556040517f8017bb887fdf8fca4314a9d40f6e73b3b81002d67e5cfa85d88173af6aa46072926108429216906153d5565b60405180910390a150565b61085960076000614f91565b805161086c906007906020840190614faf565b5050565b600654600160a01b900460ff16801561089457506006546001600160a01b03163314155b156108db576006546001600160a01b03166108c2576040516312e6d1e760e11b815260040160405180910390fd5b6040516301474c8f60e71b815260040160405180910390fd5b6108e3610d63565b5182516001600160401b0391821691161115806109215750610903610d63565b602001516001600160401b031682602001516001600160401b031611155b1561093f5760405163051c46ef60e01b815260040160405180910390fd5b600080546109649063ffffffff811690600160601b90046001600160401b031661592e565b6000805463ffffffff600160401b91829004168252600560205260409091205491925090046001600160401b03908116908216148015816109ba5750816001600160401b031684602001516001600160401b0316115b156109e3578160405163036466bf60e31b81526004016109da91906157fc565b60405180910390fd5b6109f08460400151611287565b6109fd8460600151611287565b610a0a8460800151611287565b610a178460a00151611287565b610a248460c00151611287565b8015610a3257610a326112e3565b610a3c848461142e565b60008054600160401b9081900463ffffffff168252600560208181526040808520895181548b850180516001600160401b039384166001600160801b0319909316929092179183169097021782558a8301805160018085019190915560608d015160028086019190915560808e0151600386015560a08e0151600486015560c08e01519785019790975560e08d015160069094019390935560078054808501825590895243600080516020615d5c8339815191529091015583518085018552875183168152815181870190815260088054958601815590995251600080516020615ddc8339815191529390960292830180546001600160401b031916968316969096179095559551600080516020615e1c83398151915290910155925188519251935193845284169391909116917fa04a773924505a418564363725f56832f5772e6b8d0dbd6efce724dfe803dae6910160405180910390a350505050565b610ba3614ffa565b5060008054600160201b900463ffffffff1681526005602081815260409283902083516101008101855281546001600160401b038082168352600160401b90910416928101929092526001810154938201939093526002830154606082015260038301546080820152600483015460a08201529082015460c082015260069091015460e082015290565b610c3561161e565b610c3e826116c3565b61086c82826116fa565b6000610c526117b3565b50600080516020615d9c83398151915290565b610c7160086000615051565b60005b815181101561086c576008828281518110610c9157610c91615951565b602090810291909101810151825460018082018555600094855293839020825160029092020180546001600160401b0319166001600160401b039092169190911781559101519082015501610c74565b610ce9611255565b600654600160a01b900460ff1615610d3657600680546001600160a81b03191690556040517f9a5f57de856dd668c54dd95e5c55df93432171cbca49a8776d5620ea59c0245090600090a1565b60405163a863aec960e01b815260040160405180910390fd5b565b610d59611255565b610d4f60006117fc565b610d6b614ffa565b506000805463ffffffff600160401b918290041682526005602081815260409384902084516101008101865281546001600160401b038082168352959004909416918401919091526001810154938301939093526002830154606083015260038301546080830152600483015460a083015282015460c082015260069091015460e082015290565b60408051808201909152600080825260208201526008805490610e17600183615967565b81548110610e2757610e27615951565b60009182526020909120600290910201546001600160401b03168310610e6057604051631856a49960e21b815260040160405180910390fd5b60005b81811015610ef5578360088281548110610e7f57610e7f615951565b60009182526020909120600290910201546001600160401b03161115610eed5760088181548110610eb257610eb2615951565b60009182526020918290206040805180820190915260029092020180546001600160401b031682526001015491810191909152949350505050565b600101610e63565b506008610f03600183615967565b81548110610f1357610f13615951565b60009182526020918290206040805180820190915260029092020180546001600160401b0316825260010154918101919091529392505050565b600080610f58611858565b546001600160a01b031692915050565b6000610f7261187c565b805490915060ff600160401b82041615906001600160401b0316600081158015610f995750825b90506000826001600160401b03166001148015610fb55750303b155b905081158015610fc3575080155b15610fe15760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b0319166001178555831561100a57845460ff60401b1916600160401b1785555b611013866118a0565b61101b6118b1565b60008054600160201b600160601b031916600160401b17905561103e88886118b9565b831561108957845460ff60401b191685556040517fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290611080906001906157fc565b60405180910390a15b5050505050505050565b600781815481106110a357600080fd5b600091825260209091200154905081565b60808082015160a083015160c0840151604080516020810194909452830191909152606082015260009101604051602081830303815290604052805190602001209050919050565b6008818154811061110c57600080fd5b6000918252602090912060029091020180546001909101546001600160401b03909116915082565b600754600090438411806111485750600381105b156111665760405163b0b4387760e01b815260040160405180910390fd5b60008080611175600185615967565b90505b816111e057866007828154811061119157611191615951565b9060005260206000200154116111c65760019150600781815481106111b8576111b8615951565b906000526020600020015492505b600281106111e057806111d88161597a565b915050611178565b816111fe5760405163b0b4387760e01b815260040160405180910390fd5b856112098489615967565b119450505050505b92915050565b61121f611255565b6001600160a01b038116611249576000604051631e4fbdf760e01b81526004016109da91906153d5565b611252816117fc565b50565b3361125e610f4d565b6001600160a01b031614610d4f573360405163118cdaa760e01b81526004016109da91906153d5565b600080516020615dbc83398151915281108061086c5760405162461bcd60e51b815260206004820152601b60248201527a109b8c8d4d0e881a5b9d985b1a59081cd8d85b185c88199a595b19602a1b60448201526064016109da565b6000805463ffffffff600160401b9182900416825260056020818152604080852081516101008101835281546001600160401b038082168352969004909516928501929092526001820154908401526002810154606084015260038101546080840152600481015460a08401529081015460c08301526006015460e082015261136b906110b4565b600380546001908155908290556004805460025560008054600160401b810463ffffffff16825260056020526040822060060154909255929350909190600c906113c6908490600160601b90046001600160401b0316615991565b92506101000a8154816001600160401b0302191690836001600160401b031602179055507fdb3558259e039d7e50e816b9dcce30fb114d8a9c86eca5ab14b60194d6945d3f6000600c9054906101000a90046001600160401b031660405161084291906157fc565b6000611438611bdc565b60408051600880825261012082019092529192506000919060208201610100803683370190505090506002548160008151811061147757611477615951565b60200260200101818152505083600001516001600160401b0316816001815181106114a4576114a4615951565b60200260200101818152505083602001516001600160401b0316816002815181106114d1576114d1615951565b6020026020010181815250508360400151816003815181106114f5576114f5615951565b60200260200101818152505083606001518160048151811061151957611519615951565b60209081029190910181019190915260008054600160401b900463ffffffff1681526005918290526040902060030154825190918391811061155d5761155d615951565b60209081029190910181019190915260008054600160401b900463ffffffff168152600590915260409020600401548151829060069081106115a1576115a1615951565b60209081029190910181019190915260008054600160401b900463ffffffff1681526005918290526040902001548151829060079081106115e4576115e4615951565b6020026020010181815250506115fb8282856121bd565b611618576040516309bde33960e01b815260040160405180910390fd5b50505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806116a557507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611699600080516020615d9c833981519152546001600160a01b031690565b6001600160a01b031614155b15610d4f5760405163703e46dd60e11b815260040160405180910390fd5b6116cb611255565b7ff78721226efe9a1bb678189a16d1554928b9f2192e2cb93eeda83b79fa40007d8160405161084291906153d5565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611754575060408051601f3d908101601f19168201909252611751918101906159b1565b60015b6117735781604051634c9c8ce360e01b81526004016109da91906153d5565b600080516020615d9c83398151915281146117a457604051632a87526960e21b8152600481018290526024016109da565b6117ae83836122a8565b505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610d4f5760405163703e46dd60e11b815260040160405180910390fd5b6000611806611858565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b6118a86122fe565b61125281612323565b610d4f6122fe565b81516001600160401b03161515806118dd575060208201516001600160401b031615155b806118ea57506080820151155b806118f7575060a0820151155b80611904575060c0820151155b80611911575060e0820151155b80611920575063ffffffff8116155b1561193e576040516350dd03f760e11b815260040160405180910390fd5b81600560008060049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e0820151816006015590505081600560008060089054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e08201518160060155905050600080600c6101000a8154816001600160401b0302191690836001600160401b03160217905550806000806101000a81548163ffffffff021916908363ffffffff1602179055506000611b24836110b4565b600181815560e08501516002818155600393909355600455600780548083018255600091825243600080516020615d5c833981519152909101556040805180820182526020808901516001600160401b03908116835292909801519781019788526008805494850181559092529051600080516020615ddc8339815191529290930291820180546001600160401b03191693909116929092179091559251600080516020615e1c833981519152909301929092555050565b611be4615072565b620100008152600860208201527f013d1d4b425179258b577860397955cbfa0816e32b1c25a1fd734c91b951ee816040820151527f16b88dc7439a6d841e1a1103f5a3d2d2440137f18d02763503bac7b45dcb983b6020604083015101527f0c3c864f195f59119927f53857f1de8bf575941748b735351fd31373c7875c2d6060820151527f169ba15107f2eff9b9341bf30742a838d27dbd69e88b2353dca8592f15f1111c6020606083015101527f11d4ceb15961d10b6156ae3d09bb78b4df45fb8543060884e7d400755beb4ac86080820151527f0326ff06391ed5d26ec1bc080b8d46014ee22c0c68ed022f1620c4d9d38437d36020608083015101527f23610cb43e21033c368a93622dd405b905a0eb344c98b9d7cf08b0c5ebf7c83960a0820151527e1379342a4d77d4708743aff01ff27aa11917478fdc8e2b7d463081735772ea602060a083015101527f194daf85d9eed9937b28e2a680fcc5a76922c15cd31dc4f600e14939b8200ce760c0820151527f25280b124624911c7f87b4c2d87f59c6c07e3eeeb10d614da216f6219ffe50b6602060c083015101527f04882ef39899ea38c9677a48b8f8cc6a67284e17ff940289faaa359eec9b33a660e0820151527f1bae9f36e6190738c711501be53f299bf61348e61e2ef9d577760e64f629368d602060e083015101527f2d810d30120cb93e1a254b89ed0ae86c761f49b4f129459cd534f9551851350f610100820151527f0b25394da5a1d435daccc2eadd039e2c2709f5f42fabd9afba815ed62d6af36b602061010083015101527f1c2ce7be570bea9e43f3d3d7cbca84bdb4fc89b53ae657531de72670a6105e08610120820151527f1a0255ec8c7c8769335bc9dc6b222ac6a04e766d08b45c8cc573592c05bc9c14602061012083015101527f1c164159136b8f5b4773e133f483a8a192ab15d6d3ee012f171b3d02fd4506e7610140820151527f272eb7d633cedb68ce0113f4420ab5610b81b8ba1ab9348db15761d40e8df5ba602061014083015101527f0e446639aa6caf25e93ef747084e39b8ea90abf23bb48c28fd5f9ba7ba655022610160820151527f033e195a9ea3a9ce40b72b673afb45444ca1b15f0543f44d10f5c640a780676f602061016083015101527f0e8db2b2893df23d3c55f0b3a3b54ab2e2ed775b31c4c90e472eb31582582df2610180820151527f0f4c69895451af15052aa81a03eba9752c9e7891dd08e264e0bd593d0357858e602061018083015101527f2bc091fefe2416ec83c6fba8fb82fb48cb9ca00182ec792c212bf5532958c8f76101a0820151527f1f4dcf4dd484e3ab1ae487603d2245397ac78fe6ca7584645eba3926d383902b60206101a083015101527f2daf78c5a2829a9418081dd7e8743a683ed6817996f75f10009d99220793ec736101c0820151527f19eb12a7827c0ddf6383fe806c3953bd06b08aae7bf2a01f55c986a84f50cc2860206101c083015101527f015691308846e68ea856a2cb24c9903f0c8605dea190829180ff6bdd1c6508036101e0820151527f1ffd789b155b8acb13e0f6a48b50f7aa8092540888d009141057d4569091582460206101e083015101527f0545ac7aa66dcf3719988438c806fc624de57ab43f8580392f88c86c1378ce4a610200820151527f16b7f250842ecf4e3690706a1e152d7a57f70f556f92076da785fdd363c19fcf602061020083015101527f20cb7ff35a83a7dc314036e470f14c30fb0e98d35d663b243b222caa6fc7db44610220820151527f149f415744707468bdaa4e8545201ab40d1931a7d31f23768fa7c65574ee3eab602061022083015101527f0a25c1b7573906dc4e193b4ea82fd1fe7ccebc4d925dad26f0ff09c84c9f1a75610240820151527f0a521ff30c8f3666798f847c5d4c379658fba10156e7a9499f2713fae9bf2be1602061024083015101527f03db6510c3f13629fded9a5a2d41654bbce4ef6d024cad53100051d4a3f3ebc9610260820151527f08e80a5c8e4c9b9f26f3003cc59403a18d3136afd030868d25cc8b807e2ab3706020610260830151015290565b60006121c88261232b565b6121eb836000815181106121de576121de615951565b6020026020010151611287565b612201836001815181106121de576121de615951565b612217836002815181106121de576121de615951565b61222d836003815181106121de576121de615951565b612243836004815181106121de576121de615951565b612259836005815181106121de576121de615951565b61226f836006815181106121de576121de615951565b612285836007815181106121de576121de615951565b6000612292858585612463565b905061229d816125ad565b9150505b9392505050565b6122b182612a0c565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156122f6576117ae8282612a68565b61086c612ade565b612306612afd565b610d4f57604051631afcd79f60e31b815260040160405180910390fd5b61121f6122fe565b805161233690612b17565b6123438160200151612b17565b6123508160400151612b17565b61235d8160600151612b17565b61236a8160800151612b17565b6123778160a00151612b17565b6123848160c00151612b17565b6123918160e00151612b17565b61239f816101000151612b17565b6123ad816101200151612b17565b6123bb816101400151612b17565b6123c9816101600151612b17565b6123d7816101800151612b17565b6123e5816101a00151611287565b6123f3816101c00151611287565b612401816101e00151611287565b61240f816102000151611287565b61241d816102200151611287565b61242b816102400151611287565b612439816102600151611287565b612447816102800151611287565b612455816102a00151611287565b611252816102c00151611287565b61246b615176565b8360200151835114612490576040516320fa9d8960e11b815260040160405180910390fd5b600061249d858585612ba5565b905060006124ae8660000151612ec4565b905060006124c1828460a001518861328f565b60408051601e8082526103e0820190925291925060009190602082016103c080368337505060408051601e8082526103e082019092529293506000929150602082015b61250c6151b0565b81526020019060019003908161250457905050905060006125318a858a8987876132ef565b60a0870151606087015191925090600080516020615dbc833981519152600081838509604080516101008101825260e09c8d0151815260208101969096528501525050506060810191909152608081019290925260a082015261016086015160c082015261018090950151928501929092525091949350505050565b6000600080516020615dbc8339815191526125c66151b0565b6125ce6151b0565b6040805160028082526060820183526000926020830190803683375050604080516002808252606082019092529293506000929150602082015b6126106151b0565b815260200190600190039081612608579050509050600060019050808360008151811061263f5761263f615951565b6020026020010181815250508760c001518260008151811061266357612663615951565b602002602001018190525087600001518360018151811061268657612686615951565b6020026020010181815250508760e00151826001815181106126aa576126aa615951565b60200260200101819052506126bf8284613324565b60808901515190955060609350839250905060006126de8260026159ca565b6126e99060016159ca565b9050806001600160401b0381111561270357612703615273565b60405190808252806020026020018201604052801561272c578160200160208202803683370190505b509350806001600160401b0381111561274757612747615273565b60405190808252806020026020018201604052801561278057816020015b61276d6151b0565b8152602001906001900390816127655790505b509250505060008060005b89608001515181101561282457896080015181815181106127ae576127ae615951565b60200260200101518583815181106127c8576127c8615951565b6020026020010181815250508960a0015181815181106127ea576127ea615951565b602002602001015184838151811061280457612804615951565b602090810291909101015261281a6001836159ca565b915060010161278b565b50886020015184828151811061283c5761283c615951565b6020026020010181815250508860c0015183828151811061285f5761285f615951565b60209081029190910101526128756001826159ca565b895160408b01519192509060008982840990508087858151811061289b5761289b615951565b6020026020010181815250505050508860e001518382815181106128c1576128c1615951565b60209081029190910101526128d76001826159ca565b60608a0151909150878184089250506128ef82613412565b84828151811061290157612901615951565b602002602001018181525050612915613444565b83828151811061292757612927615951565b602002602001018190525061294461293f8486613324565b613465565b945050505050600060405180608001604052807f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081526020017f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181526020017f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e5581526020017f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe48152509050612a028382846129fd6134d2565b6135a3565b9695505050505050565b806001600160a01b03163b600003612a395780604051634c9c8ce360e01b81526004016109da91906153d5565b600080516020615d9c83398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b031684604051612a8591906159dd565b600060405180830381855af49150503d8060008114612ac0576040519150601f19603f3d011682016040523d82523d6000602084013e612ac5565b606091505b5091509150612ad5858383613686565b95945050505050565b3415610d4f5760405163b398979f60e01b815260040160405180910390fd5b6000612b0761187c565b54600160401b900460ff16919050565b6000600080516020615d3c833981519152612b31836136d9565b15612b3b57505050565b8251602084015182600384858586098509088382830914838210848410161693505050816117ae5760405162461bcd60e51b8152602060048201526017602482015276109b8c8d4d0e881a5b9d985b1a590811cc481c1bda5b9d604a1b60448201526064016109da565b612bed60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604080518082019091526060815260006020820152600080516020615dbc833981519152612c1c8287876136e8565b81518451612c2990613a81565b612c368660200151613a81565b612c438760400151613a81565b612c508860600151613a81565b612c5d8960800151613a81565b604051602001612c72969594939291906159f9565b60408051601f198184030181529190528252612c8d82613af6565b50612c9782613af6565b6060840152612ca582613af6565b6080840152815160a0850151612cba90613a81565b604051602001612ccb929190615a78565b60408051601f198184030181529190528252612ce682613af6565b8352815160c0850151612cf890613a81565b612d058660e00151613a81565b612d13876101000151613a81565b612d21886101200151613a81565b612d2f896101400151613a81565b604051602001612d44969594939291906159f9565b60408051601f198184030181529190528252612d5f82613af6565b60a084015281516101a0850151612d7590613b58565b612d83866101c00151613b58565b612d91876101e00151613b58565b612d9f886102000151613b58565b612dad896102200151613b58565b604051602001612dc296959493929190615aa7565b60408051601f19818403018152919052808352610240850151612de490613b58565b612df2866102600151613b58565b612e00876102800151613b58565b612e0e886102a00151613b58565b612e1c896102c00151613b58565b604051602001612e3196959493929190615aa7565b60408051601f198184030181529190528252612e4c82613af6565b60c08401528151610160850151612e6290613a81565b612e70866101800151613a81565b604051602001612e8293929190615ae3565b60408051601f198184030181529190528252612e9d82613af6565b60e08401528251818180098282820960208601919091526040850152509195945050505050565b612ef66040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b816201000003612f8a57506040805160a0810182526010815260208101929092527f30641e0e92bebef818268d663bcad6dbcfd6c0149170f6d7d350b1b1fa6c1001908201527eeeb2cb5981ed45649abebde081dcff16c8601de4347e7dd1628ba2daac43b760608201527f0b5d56b77fe704e8e92338c0082f37e091126414c830e4c6922d5ac802d842d4608082015290565b81620200000361301f57506040805160a0810182526011815260208101929092527f30643640b9f82f90e83b698e5ea6179c7c05542e859533b48b9953a2f5360801908201527f1bf82deba7d74902c3708cc6e70e61f30512eca95655210e276e5858ce8f58e560608201527f244cf010c43ca87237d8b00bf9dd50c4c01c7f086bd4e8c920e75251d96f0d22608082015290565b8162040000036130b457506040805160a0810182526012815260208101929092527f30644259cd94e7dd5045d7a27013b7fcd21c9e3b7fa75222e7bda49b729b0401908201527f19ddbcaf3a8d46c15c0176fbb5b95e4dc57088ff13f4d1bd84c6bfa57dcdc0e060608201527f036853f083780e87f8d7c71d111119c57dbe118c22d5ad707a82317466c5174c608082015290565b81620800000361314957506040805160a0810182526013815260208101929092527f3064486657634403844b0eac78ca882cfd284341fcb0615a15cfcd17b14d8201908201527f2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd60608201527f06e402c0a314fb67a15cf806664ae1b722dbc0efe66e6c81d98f9924ca535321608082015290565b8162100000036131de57506040805160a0810182526014815260208101929092527f30644b6c9c4a72169e4daa317d25f04512ae15c53b34e8f5acd8e155d0a6c101908201527f26125da10a0ed06327508aba06d1e303ac616632dbed349f53422da95333785760608201527f100c332d2100895fab6473bc2c51bfca521f45cb3baca6260852a8fde26c91f3608082015290565b8160200361327157506040805160a0810182526005815260208101929092527f2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001908201527f09c532c6306b93d29678200d47c0b2a99c18d51b838eeb1d3eed4c533bb512d060608201527f2724713603bfbd790aeaf3e7df25d8e7ef8f311334905b4d8c99980cf210979d608082015290565b60405163e2ef09e560e01b815260040160405180910390fd5b919050565b6132b360405180606001604052806000815260200160008152602001600081525090565b6132bd8484613c8f565b8082526132cd9085908590613ce3565b602082015280516132e390859084908690613d57565b60408201529392505050565b6000806132fd858789613f0b565b905061330d888689898888613ff7565b613318818786614303565b98975050505050505050565b61332c6151b0565b825182511461337d5760405162461bcd60e51b815260206004820181905260248201527f4d534d206572726f723a206c656e67746820646f6573206e6f74206d6174636860448201526064016109da565b6133bb8360008151811061339357613393615951565b6020026020010151836000815181106133ae576133ae615951565b6020026020010151614353565b905060015b825181101561340b57613401826133fc8684815181106133e2576133e2615951565b60200260200101518685815181106133ae576133ae615951565b6143e7565b91506001016133c0565b5092915050565b600061342c600080516020615dbc83398151915283615b3c565b61121190600080516020615dbc833981519152615967565b61344c6151b0565b5060408051808201909152600181526002602082015290565b61346d6151b0565b613476826136d9565b1561347f575090565b604051806040016040528083600001518152602001600080516020615d3c83398151915284602001516134b29190615b3c565b6134ca90600080516020615d3c833981519152615967565b905292915050565b6134fd6040518060800160405280600081526020016000815260200160008152602001600081525090565b60405180608001604052807f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed81526020017f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c281526020017f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa81526020017f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b815250905090565b60008060006040518751815260208801516020820152602087015160408201528651606082015260608701516080820152604087015160a0820152855160c0820152602086015160e08201526020850151610100820152845161012082015260608501516101408201526040850151610160820152602060006101808360085afa9150506000519150806136785760405162461bcd60e51b815260206004820152601c60248201527b426e3235343a2050616972696e6720636865636b206661696c65642160201b60448201526064016109da565b50151590505b949350505050565b60608261369b5761369682614482565b6122a1565b81511580156136b257506001600160a01b0384163b155b156136d25783604051639996b31560e01b81526004016109da91906153d5565b50806122a1565b80516020909101511590151690565b825160fe906137236136f983613b58565b60405160200161370b91815260200190565b604051602081830303815290604052600060046144ab565b61375d6137338660000151613b58565b60405160200161374591815260200190565b604051602081830303815290604052600060086144ab565b61376d6137338760200151613b58565b6040516020016137809493929190615b5e565b60408051601f1981840301815291905280855261379d6001613b58565b6137b4600080516020615e5c833981519152613b58565b6137cb600080516020615dfc833981519152613b58565b6137e2600080516020615e3c833981519152613b58565b6137f9600080516020615d7c833981519152613b58565b60405160200161380e96959493929190615aa7565b60408051601f1981840301815291905280855260e084015161382f90613a81565b61383d856101000151613a81565b61384b866101200151613a81565b613859876101400151613a81565b613867886101600151613a81565b613875896101800151613a81565b6138838a6101e00151613a81565b60405160200161389a989796959493929190615bb5565b60408051601f198184030181529190528085526102008401516138bc90613a81565b6138ca856102200151613a81565b6138d8866102400151613a81565b6138e6876101a00151613a81565b6138f4886101c00151613a81565b613902896102600151613a81565b6040516020016139189796959493929190615c5a565b60408051601f1981840301815291815281865284015161393790613a81565b6139448560600151613a81565b6139518660800151613a81565b61395e8760a00151613a81565b61396b8860c00151613a81565b604051602001613980969594939291906159f9565b60408051601f1981840301815291905280855282516139b79084906000906139aa576139aa615951565b6020026020010151613b58565b6139cd846001815181106139aa576139aa615951565b6139e3856002815181106139aa576139aa615951565b6139f9866003815181106139aa576139aa615951565b613a0f876004815181106139aa576139aa615951565b613a25886005815181106139aa576139aa615951565b613a3b896006815181106139aa576139aa615951565b613a518a6007815181106139aa576139aa615951565b604051602001613a6999989796959493929190615cec565b60408051601f19818403018152919052909352505050565b60606000613a8e836136d9565b15613a9a57600160fe1b175b6020830151600080516020615d3c83398151915260019190911b10613ac05750600160ff1b5b8251613acd908217613b58565b604051602001613adf91815260200190565b604051602081830303815290604052915050919050565b602080820151825180516040518381526000948594939291908101855b83811015613b2d5760208186018101518383015201613b13565b50506020918201902090860181905292506000612a02600080516020615dbc83398151915285615b3c565b60008190506008817eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff16901b6008827fff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0016901c1790506010817dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff16901b6010827dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff1916901c1790506020817bffffffff00000000ffffffff00000000ffffffff00000000ffffffff16901b6020827bffffffff00000000ffffffff00000000ffffffff00000000ffffffff1916901c1790506040816001600160401b03600160801b03600160c01b0316901b6040826001600160401b03600160801b03600160c01b031916901c179050608081901b608082901c179050919050565b8151600090600080516020615dbc83398151915290838015613cd35784935060005b82811015613cc757838586099450600101613cb1565b50600184039350613cda565b6001830393505b50505092915050565b600082600103613cf5575060016122a1565b81600003613d05575060006122a1565b6040840151600080516020615dbc83398151915290600090828186099050858015613d3557600187039250613d3c565b6001840392505b50613d46826145b8565b915082828209979650505050505050565b8251600090600080516020615dbc83398151915290838303613dd857600160005b82811015613dcb57818703613dac57878181518110613d9957613d99615951565b602002602001015194505050505061367e565b8380613dba57613dba615b26565b896060015183099150600101613d78565b506000935050505061367e565b6000806000808a604001519050600080613df28d8861465e565b90506000876001600160401b03811115613e0e57613e0e615273565b604051908082528060200260200182016040528015613e37578160200160208202803683370190505b509050888b850993506001925060005b88811015613e7c57602081026020840101519550898d878c030896508987850960208281028401018890529350600101613e47565b50613e86836145b8565b925060005b88811015613ef95760208102602084010151955089868609975089848909975060005b89811015613ed857808214613ed0576020810260208401015197508a888a0998505b600101613eae565b506020810260208f010151955089868909975089888c089a50600101613e8b565b50505050505050505050949350505050565b600080600080516020615dbc8339815191529050600083602001519050600084604001519050600060019050606088015160808901516101a08901516102408a0151878889838709858501088609945050506101c08901516102608a0151878889838709858501088609945050506101e08901516102808a0151878889838709858501088609945050506102008901516102a08a01518788898387098585010886099450505061022089015191506102c0890151868782898587080985099350505050875160208901518586868309870385089650508485838309860387089998505050505050505050565b614005868686868587614723565b60c08501518251600080516020615dbc8339815191529190819081908690601490811061403457614034615951565b60200260200101818152505085600001518460148151811061405857614058615951565b6020026020010181905250828282099050808560158151811061407d5761407d615951565b6020026020010181815250508560200151846015815181106140a1576140a1615951565b602002602001018190525082828209905080856016815181106140c6576140c6615951565b6020026020010181815250508560400151846016815181106140ea576140ea615951565b6020026020010181905250828282099050808560178151811061410f5761410f615951565b60200260200101818152505085606001518460178151811061413357614133615951565b6020026020010181905250828282099050808560188151811061415857614158615951565b60200260200101818152505085608001518460188151811061417c5761417c615951565b602002602001018190525082828209905080856019815181106141a1576141a1615951565b6020026020010181815250508860400151846019815181106141c5576141c5615951565b60200260200101819052508282820990508085601a815181106141ea576141ea615951565b602002602001018181525050886060015184601a8151811061420e5761420e615951565b60200260200101819052508282820990508085601b8151811061423357614233615951565b602002602001018181525050886080015184601b8151811061425757614257615951565b60200260200101819052508282820990508085601c8151811061427c5761427c615951565b6020026020010181815250508860a0015184601c815181106142a0576142a0615951565b60200260200101819052508282820990508760e0015185601d815181106142c9576142c9615951565b6020026020010181815250508560a0015184601d815181106142ed576142ed615951565b6020026020010181905250505050505050505050565b600080516020615dbc8339815191528381039060005b600a81101561434a5760206015820102840151602082026101a0018601518384828409860894505050600101614319565b50509392505050565b61435b6151b0565b6143636151ca565b835181526020808501519082015260408101839052600060608360808460076107d05a03fa9050808061439557600080fd5b50806143df5760405162461bcd60e51b8152602060048201526019602482015278426e3235343a207363616c6172206d756c206661696c65642160381b60448201526064016109da565b505092915050565b6143ef6151b0565b6143f76151e8565b8351815260208085015181830152835160408301528301516060808301919091526000908360c08460066107d05a03fa9050808061443457600080fd5b50806143df5760405162461bcd60e51b815260206004820152601d60248201527f426e3235343a2067726f7570206164646974696f6e206661696c65642100000060448201526064016109da565b8051156144925780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6060816144b981601f6159ca565b10156144f85760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016109da565b61450282846159ca565b845110156145465760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b60448201526064016109da565b60608215801561456557604051915060008252602082016040526145af565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561459e578051835260209283019201614586565b5050858452601f01601f1916604052505b50949350505050565b6000806000600080516020615dbc833981519152905060405160208152602080820152602060408201528460608201526002820360808201528160a08201526020600060c08360055afa9250506000519250816146575760405162461bcd60e51b815260206004820152601d60248201527f426e3235343a20706f7720707265636f6d70696c65206661696c65642100000060448201526064016109da565b5050919050565b6060826020015182111561468557604051638c5e11f160e01b815260040160405180910390fd5b60608301516001600080516020615dbc833981519152846001600160401b038111156146b3576146b3615273565b6040519080825280602002602001820160405280156146dc578160200160208202803683370190505b50935084151915613cda5760208401856020028101600182526020820191505b80821015614718578285850993508382526020820191506146fc565b505050505092915050565b600080600080600080600080516020615dbc83398151915290508060208b015160208d01510995508a5193508060a08c015160608d0151099250806101a08a0151840891508060808c01518308915080848309935080600080516020615e5c83398151915284099150806101c08a0151830891508060808c01518308915080848309935080600080516020615dfc83398151915284099150806101e08a0151830891508060808c01518308915080848309935080600080516020615e3c83398151915284099150806102008a0151830891508060808c01518308915080848309935080600080516020615d7c83398151915284099150806102208a0151830891508060808c0151830891508084830993508084870895508860a001518860008151811061485257614852615951565b6020026020010181905250858760008151811061487157614871615951565b6020026020010181815250508060608c01518c51099450806102c08a015186099450806102408a015160608d0151099250806101a08a0151840892508060808c015184089250808386099450806102608a015160608d0151099250806101c08a0151840892508060808c015184089250808386099450806102808a015160608d0151099250806101e08a0151840892508060808c015184089250808386099450806102a08a015160608d0151099250806102008a0151840892508060808c0151840892508083860994508b60c001518860018151811061495357614953615951565b60209081029190910101526149688582615967565b8760018151811061497b5761497b615951565b602002602001018181525050886101a00151876002815181106149a0576149a0615951565b602002602001018181525050886101c00151876003815181106149c5576149c5615951565b602002602001018181525050886101e00151876004815181106149ea576149ea615951565b60200260200101818152505088610200015187600581518110614a0f57614a0f615951565b6020026020010181815250508b60e0015188600281518110614a3357614a33615951565b60200260200101819052508b610100015188600381518110614a5757614a57615951565b60200260200101819052508b610120015188600481518110614a7b57614a7b615951565b60200260200101819052508b610140015188600581518110614a9f57614a9f615951565b6020026020010181905250806101c08a01516101a08b01510992508287600681518110614ace57614ace615951565b6020026020010181815250508b610160015188600681518110614af357614af3615951565b6020026020010181905250806102008a01516101e08b01510992508287600781518110614b2257614b22615951565b6020026020010181815250508b610180015188600781518110614b4757614b47615951565b60200260200101819052506101a089015192508083840991508082830991508082840992508287600881518110614b8057614b80615951565b6020026020010181815250508b6101e0015188600881518110614ba557614ba5615951565b60200260200101819052506101c089015192508083840991508082830991508082840992508287600981518110614bde57614bde615951565b6020026020010181815250508b610200015188600981518110614c0357614c03615951565b60200260200101819052506101e089015192508083840991508082830991508082840992508287600a81518110614c3c57614c3c615951565b6020026020010181815250508b610220015188600a81518110614c6157614c61615951565b602002602001018190525061020089015192508083840991508082830991508082840992508287600b81518110614c9a57614c9a615951565b6020026020010181815250508b610240015188600b81518110614cbf57614cbf615951565b602002602001018190525088610220015181614cdb9190615967565b87600c81518110614cee57614cee615951565b6020026020010181815250508b6101a0015188600c81518110614d1357614d13615951565b6020026020010181905250600187600d81518110614d3357614d33615951565b6020026020010181815250508b6101c0015188600d81518110614d5857614d58615951565b6020026020010181905250806101c08a01516101a08b0151099250806101e08a015184099250806102008a015184099250806102208a0151840992508287600e81518110614da857614da8615951565b6020026020010181815250508b610260015188600e81518110614dcd57614dcd615951565b60209081029190910101528951614de49082615967565b87600f81518110614df757614df7615951565b6020026020010181815250508860c0015188600f81518110614e1b57614e1b615951565b60200260200101819052508060018b510860a08c0151909350819080099150808284099250808360206010028901510991508187601081518110614e6157614e61615951565b6020026020010181815250508860e0015188601081518110614e8557614e85615951565b6020026020010181905250808360206011028901510991508187601181518110614eb157614eb1615951565b60200260200101818152505088610100015188601181518110614ed657614ed6615951565b6020026020010181905250808360206012028901510991508187601281518110614f0257614f02615951565b60200260200101818152505088610120015188601281518110614f2757614f27615951565b6020026020010181905250808360206013028901510991508187601381518110614f5357614f53615951565b60200260200101818152505088610140015188601381518110614f7857614f78615951565b6020026020010181905250505050505050505050505050565b50805460008255906000526020600020908101906112529190615206565b828054828255906000526020600020908101928215614fea579160200282015b82811115614fea578251825591602001919060010190614fcf565b50614ff6929150615206565b5090565b60405180610100016040528060006001600160401b0316815260200160006001600160401b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5080546000825560020290600052602060002090810190611252919061521b565b60405180610280016040528060008152602001600081526020016150946151b0565b81526020016150a16151b0565b81526020016150ae6151b0565b81526020016150bb6151b0565b81526020016150c86151b0565b81526020016150d56151b0565b81526020016150e26151b0565b81526020016150ef6151b0565b81526020016150fc6151b0565b81526020016151096151b0565b81526020016151166151b0565b81526020016151236151b0565b81526020016151306151b0565b815260200161513d6151b0565b815260200161514a6151b0565b81526020016151576151b0565b81526020016151646151b0565b81526020016151716151b0565b905290565b6040518061010001604052806000815260200160008152602001600081526020016000815260200160608152602001606081526020016151645b604051806040016040528060008152602001600081525090565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b5b80821115614ff65760008155600101615207565b5b80821115614ff65780546001600160401b03191681556000600182015560020161521c565b80356001600160a01b038116811461328a57600080fd5b60006020828403121561526a57600080fd5b6122a182615241565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156152ab576152ab615273565b60405290565b6040516102e081016001600160401b03811182821017156152ab576152ab615273565b604051601f8201601f191681016001600160401b03811182821017156152fc576152fc615273565b604052919050565b80356001600160401b038116811461328a57600080fd5b600061010080838503121561532f57600080fd5b604051908101906001600160401b038211818310171561535157615351615273565b8160405280925061536184615304565b815261536f60208501615304565b602082015260408401356040820152606084013560608201526080840135608082015260a084013560a082015260c084013560c082015260e084013560e0820152505092915050565b600061010082840312156153cb57600080fd5b6122a1838361531b565b6001600160a01b0391909116815260200190565b60006001600160401b0382111561540257615402615273565b5060051b60200190565b6000602080838503121561541f57600080fd5b82356001600160401b0381111561543557600080fd5b8301601f8101851361544657600080fd5b8035615459615454826153e9565b6152d4565b81815260059190911b8201830190838101908783111561547857600080fd5b928401925b828410156154965783358252928401929084019061547d565b979650505050505050565b6000602082840312156154b357600080fd5b6122a182615304565b6000604082840312156154ce57600080fd5b6154d6615289565b9050813581526020820135602082015292915050565b60008082840361058081121561550157600080fd5b61550b858561531b565b92506101006104808060ff198401121561552457600080fd5b61552c6152b1565b925061553a878388016154bc565b835261014061554b888289016154bc565b602085015261018061555f89828a016154bc565b60408601526101c06155738a828b016154bc565b60608701526102006155878b828c016154bc565b608088015261024061559b8c828d016154bc565b60a08901526102806155af8d828e016154bc565b60c08a01526102c06155c38e828f016154bc565b60e08b01526155d68e6103008f016154bc565b898b01526155e88e6103408f016154bc565b6101208b01526155fc8e6103808f016154bc565b878b015261560e8e6103c08f016154bc565b6101608b01526156228e6104008f016154bc565b868b01526104408d01356101a08b01526104608d0135858b0152878d01356101e08b01526104a08d0135848b01526104c08d01356102208b01526104e08d0135838b01526105008d01356102608b01526105208d0135828b01526105408d01356102a08b01526105608d0135818b0152505050505050505050809150509250929050565b600080604083850312156156b957600080fd5b6156c283615241565b91506020838101356001600160401b03808211156156df57600080fd5b818601915086601f8301126156f357600080fd5b81358181111561570557615705615273565b615717601f8201601f191685016152d4565b9150808252878482850101111561572d57600080fd5b80848401858401376000848284010152508093505050509250929050565b6000602080838503121561575e57600080fd5b82356001600160401b0381111561577457600080fd5b8301601f8101851361578557600080fd5b8035615793615454826153e9565b81815260069190911b820183019083810190878311156157b257600080fd5b928401925b8284101561549657604084890312156157d05760008081fd5b6157d8615289565b6157e185615304565b815284860135868201528252604090930192908401906157b7565b6001600160401b0391909116815260200190565b803563ffffffff8116811461328a57600080fd5b60006020828403121561583657600080fd5b6122a182615810565b60006020828403121561585157600080fd5b5035919050565b6000806000610140848603121561586e57600080fd5b615878858561531b565b92506158876101008501615810565b91506158966101208501615241565b90509250925092565b60005b838110156158ba5781810151838201526020016158a2565b50506000910152565b60208152600082518060208401526158e281604085016020870161589f565b601f01601f19169190910160400192915050565b6000806040838503121561590957600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6001600160401b038181168382160280821691908281146143df576143df615918565b634e487b7160e01b600052603260045260246000fd5b8181038181111561121157611211615918565b60008161598957615989615918565b506000190190565b6001600160401b0381811683821601908082111561340b5761340b615918565b6000602082840312156159c357600080fd5b5051919050565b8082018082111561121157611211615918565b600082516159ef81846020870161589f565b9190910192915050565b600087516020615a0c8285838d0161589f565b885191840191615a1f8184848d0161589f565b8851920191615a318184848c0161589f565b8751920191615a438184848b0161589f565b8651920191615a558184848a0161589f565b8551920191615a67818484890161589f565b919091019998505050505050505050565b60008351615a8a81846020880161589f565b835190830190615a9e81836020880161589f565b01949350505050565b60008751615ab9818460208c0161589f565b9190910195865250602085019390935260408401919091526060830152608082015260a001919050565b60008451615af581846020890161589f565b845190830190615b0981836020890161589f565b8451910190615b1c81836020880161589f565b0195945050505050565b634e487b7160e01b600052601260045260246000fd5b600082615b5957634e487b7160e01b600052601260045260246000fd5b500690565b60008551615b70818460208a0161589f565b855190830190615b84818360208a0161589f565b8551910190615b9781836020890161589f565b8451910190615baa81836020880161589f565b019695505050505050565b600089516020615bc88285838f0161589f565b8a5191840191615bdb8184848f0161589f565b8a51920191615bed8184848e0161589f565b8951920191615bff8184848d0161589f565b8851920191615c118184848c0161589f565b8751920191615c238184848b0161589f565b8651920191615c358184848a0161589f565b8551920191615c47818484890161589f565b919091019b9a5050505050505050505050565b600088516020615c6d8285838e0161589f565b895191840191615c808184848e0161589f565b8951920191615c928184848d0161589f565b8851920191615ca48184848c0161589f565b8751920191615cb68184848b0161589f565b8651920191615cc88184848a0161589f565b8551920191615cda818484890161589f565b919091019a9950505050505050505050565b60008a51615cfe818460208f0161589f565b9190910198895250602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000191905056fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6882e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e881360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001f3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee31ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb025f3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee42042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4aa164736f6c6343000817000a" diff --git a/contract-bindings/artifacts/LightClient_bytecode.json b/contract-bindings/artifacts/LightClient_bytecode.json index 70a55867e..ecc8c2165 100644 --- a/contract-bindings/artifacts/LightClient_bytecode.json +++ b/contract-bindings/artifacts/LightClient_bytecode.json @@ -1 +1 @@ -"0x60a0604052306080523480156200001557600080fd5b506200002062000026565b620000da565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000775760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b608051615d9b62000104600039600081816113d0015281816113f901526115e70152615d9b6000f3fe6080604052600436106101805760003560e01c806376671808116100d6578063aa9227321161007f578063ca6fe85511610059578063ca6fe855146105c8578063f0682054146105de578063f2fde38b1461061057600080fd5b8063aa922732146104cd578063ad3cb1cc14610530578063bd32519a1461058657600080fd5b806382d07ff3116100b057806382d07ff31461045b5780638da5cb5b14610470578063a244d596146104ad57600080fd5b8063766718081461034157806376b6b7cb1461038b5780637f17baad146103a157600080fd5b80634847ae5d116101385780636282773311610112578063628277331461030157806369cc6a0414610317578063715018a61461032c57600080fd5b80634847ae5d146102555780634f1ef286146102d957806352d1902d146102ec57600080fd5b8063313df7b111610169578063313df7b1146101d9578063382b215a14610211578063409939b71461023557600080fd5b8063013fa5fc146101855780630d8e6e2c146101a7575b600080fd5b34801561019157600080fd5b506101a56101a0366004615487565b610630565b005b3480156101b357600080fd5b506040805160018152600060208201819052918101919091526060015b60405180910390f35b3480156101e557600080fd5b506006546101f9906001600160a01b031681565b6040516001600160a01b0390911681526020016101d0565b34801561021d57600080fd5b5061022760035481565b6040519081526020016101d0565b34801561024157600080fd5b506101a5610250366004615618565b610748565b34801561026157600080fd5b5061026a610a8a565b6040516101d0919060006101008201905067ffffffffffffffff8084511683528060208501511660208401525060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6101a56102e73660046157f0565b610b77565b3480156102f857600080fd5b50610227610b96565b34801561030d57600080fd5b5061022760025481565b34801561032357600080fd5b506101a5610bc5565b34801561033857600080fd5b506101a5610c77565b34801561034d57600080fd5b50600054610372906c01000000000000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101d0565b34801561039757600080fd5b5061022760015481565b3480156103ad57600080fd5b506104146103bc3660046158aa565b6005602081905260009182526040909120805460018201546002830154600384015460048501549585015460069095015467ffffffffffffffff80861697680100000000000000009096041695939492939192919088565b6040805167ffffffffffffffff998a168152989097166020890152958701949094526060860192909252608085015260a084015260c083015260e0820152610100016101d0565b34801561046757600080fd5b5061026a610c89565b34801561047c57600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166101f9565b3480156104b957600080fd5b506101a56104c83660046158c5565b610d73565b3480156104d957600080fd5b506102276104e836600461590c565b60808082015160a083015160c0840151604080516020810194909452830191909152606082015260009101604051602081830303815290604052805190602001209050919050565b34801561053c57600080fd5b506105796040518060400160405280600581526020017f352e302e3000000000000000000000000000000000000000000000000000000081525081565b6040516101d0919061594d565b34801561059257600080fd5b506006546105b89074010000000000000000000000000000000000000000900460ff1681565b60405190151581526020016101d0565b3480156105d457600080fd5b5061022760045481565b3480156105ea57600080fd5b506000546105fb9063ffffffff1681565b60405163ffffffff90911681526020016101d0565b34801561061c57600080fd5b506101a561062b366004615487565b610f0e565b610638610f65565b6001600160a01b038116610678576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546001600160a01b03908116908216036106c0576040517fa863aec900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffff000000000000000000000000000000000000000000166001600160a01b0383811691909117740100000000000000000000000000000000000000001791829055604051911681527f8017bb887fdf8fca4314a9d40f6e73b3b81002d67e5cfa85d88173af6aa46072906020015b60405180910390a150565b60065474010000000000000000000000000000000000000000900460ff16801561077d57506006546001600160a01b03163314155b156107f6576006546001600160a01b03166107c4576040517f25cda3ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa3a6478000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107fe610c89565b51825167ffffffffffffffff918216911611158061083f575061081f610c89565b6020015167ffffffffffffffff16826020015167ffffffffffffffff1611155b15610876576040517f051c46ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080546108a59063ffffffff8116906c01000000000000000000000000900467ffffffffffffffff16615996565b6000805463ffffffff68010000000000000000918290041682526005602052604090912054919250900467ffffffffffffffff9081169082161480158161090357508167ffffffffffffffff16846020015167ffffffffffffffff16115b1561094b576040517f1b2335f800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff831660048201526024015b60405180910390fd5b6109588460400151610fd9565b6109658460600151610fd9565b6109728460800151610fd9565b61097f8460a00151610fd9565b61098c8460c00151610fd9565b801561099a5761099a611049565b6109a484846111ab565b6000805463ffffffff680100000000000000009182900416825260056020818152604093849020885181548a84015167ffffffffffffffff9081169687027fffffffffffffffffffffffffffffffff000000000000000000000000000000009092169216918217178255898601516001830181905560608b0151600284015560808b0151600384015560a08b0151600484015560c08b01519483019490945560e08a015160069092019190915593519182529192917fa04a773924505a418564363725f56832f5772e6b8d0dbd6efce724dfe803dae6910160405180910390a350505050565b610ae6604051806101000160405280600067ffffffffffffffff168152602001600067ffffffffffffffff1681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5060008054640100000000900463ffffffff16815260056020818152604092839020835161010081018552815467ffffffffffffffff80821683526801000000000000000090910416928101929092526001810154938201939093526002830154606082015260038301546080820152600483015460a08201529082015460c082015260069091015460e082015290565b610b7f6113c5565b610b8882611495565b610b9282826114d6565b5050565b6000610ba06115dc565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b610bcd610f65565b60065474010000000000000000000000000000000000000000900460ff1615610c4357600680547fffffffffffffffffffffff0000000000000000000000000000000000000000001690556040517f9a5f57de856dd668c54dd95e5c55df93432171cbca49a8776d5620ea59c0245090600090a1565b6040517fa863aec900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b610c7f610f65565b610c75600061163e565b610ce5604051806101000160405280600067ffffffffffffffff168152602001600067ffffffffffffffff1681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b506000805463ffffffff680100000000000000009182900416825260056020818152604093849020845161010081018652815467ffffffffffffffff8082168352959004909416918401919091526001810154938301939093526002830154606083015260038301546080830152600483015460a083015282015460c082015260069091015460e082015290565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610dbe5750825b905060008267ffffffffffffffff166001148015610ddb5750303b155b905081158015610de9575080155b15610e20576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610e6b57845468ff00000000000000001916680100000000000000001785555b610e74866116c7565b610e7c6116d8565b600080547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff1668010000000000000000179055610eb988886116e0565b8315610f0457845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b610f16610f65565b6001600160a01b038116610f59576040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260006004820152602401610942565b610f628161163e565b50565b33610f977f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610c75576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610942565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001811080610b925760405162461bcd60e51b815260206004820152601b60248201527f426e3235343a20696e76616c6964207363616c6172206669656c6400000000006044820152606401610942565b600080546801000000000000000080820463ffffffff16808452600560208181526040808720815161010081018352815467ffffffffffffffff808216835297900487168185015260018083015482850152600280840154606080850191909152600380860154608080870182905260048089015460a08901819052898d015460c08a018190526006909a01805460e0909a01999099528a51808d0194909452838b015282850198909852885180830390940184520190965280519087012085548355948590558354905595895293909252915490559390929091600c916111439185916c010000000000000000000000009004166159ba565b82546101009290920a67ffffffffffffffff8181021990931691831602179091556000546040516c0100000000000000000000000090910490911681527fdb3558259e039d7e50e816b9dcce30fb114d8a9c86eca5ab14b60194d6945d3f915060200161073d565b60006111b56119d0565b6040805160088082526101208201909252919250600091906020820161010080368337019050509050600254816000815181106111f4576111f46159db565b602002602001018181525050836000015167ffffffffffffffff1681600181518110611222576112226159db565b602002602001018181525050836020015167ffffffffffffffff1681600281518110611250576112506159db565b602002602001018181525050836040015181600381518110611274576112746159db565b602002602001018181525050836060015181600481518110611298576112986159db565b6020908102919091018101919091526000805468010000000000000000900463ffffffff168152600591829052604090206003015482519091839181106112e1576112e16159db565b6020908102919091018101919091526000805468010000000000000000900463ffffffff1681526005909152604090206004015481518290600690811061132a5761132a6159db565b6020908102919091018101919091526000805468010000000000000000900463ffffffff168152600591829052604090200154815182906007908110611372576113726159db565b602002602001018181525050611389828285611fb2565b6113bf576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148061145e57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166114527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b031614155b15610c75576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61149d610f65565b6040516001600160a01b03821681527ff78721226efe9a1bb678189a16d1554928b9f2192e2cb93eeda83b79fa40007d9060200161073d565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611530575060408051601f3d908101601f1916820190925261152d918101906159f1565b60015b611571576040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610942565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146115cd576040517faa1d49a400000000000000000000000000000000000000000000000000000000815260048101829052602401610942565b6115d7838361209d565b505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610c75576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6116cf6120f3565b610f628161215a565b610c756120f3565b815167ffffffffffffffff161515806117065750602082015167ffffffffffffffff1615155b8061171357506080820151155b80611720575060a0820151155b8061172d575060c0820151155b8061173a575060e0820151155b80611749575063ffffffff8116155b15611780576040517fa1ba07ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600560008060049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160000160086101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e0820151816006015590505081600560008060089054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160000160086101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e08201518160060155905050600080600c6101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550806000806101000a81548163ffffffff021916908363ffffffff16021790555060006119b38360808082015160a083015160c0840151604080516020810194909452830191909152606082015260009101604051602081830303815290604052805190602001209050919050565b600181905560e09093015160028190556003939093555050600455565b6119d8615163565b621000008152600860208201527f20c9403133dfde9a9d382df76fb0523571648725abc0a7c12830bb690ec83b336040820151527f03a0a9acc3e3815a7ed6cb1379f7d157e6343164729376392a693acbd3ec283c6020604083015101527f2866c18ad1df10ef13542cce6250ce02cb2a6b72ae00a9852e271187e9e4e0db6060820151527f21be232a42246a5663ebf483470cca666ffe9d4f0e63b929c596a7658714e9706020606083015101527f07d77873b9860074118e75808c79468b83c8ed64ba14db5cb5afa8e534de7b996080820151527f0be0f448839080132d47de17de0099b4cd74ae1e6b71cdda06cdebb868a50c6d6020608083015101527f13bd45a023491eadea44cc3f24cfbd1796eade9c0e39ee81d9f63ea0a580662560a0820151527f18f95cdda42ce11d9d10a3b335acc214e3807c578c5359405d810c208df60093602060a083015101527f0970d978763461f09e9ec63454073497386e4d282fedc2ac5b967cb9fd3fa8a960c0820151527f28c2217f7bacf6f8b2b8ee4a90fcf8b5bca04205ea84e8e1eb54b85dd41bde28602060c083015101527f02fe3d02988db718380052970aba46a3296df5f29b736ba1f2c4ccffc8b5969360e0820151527f202c3e390cee7c5c8525da2329a19f4936f6f71ca97dde6c6fa32b382d5acc03602060e083015101527f23ac10ae6ca5cacee8744bb939aaa835390954b91ae668a2c8d0edda558a89e7610100820151527f1c8c2b856cdade256ba3237f39afd5e170a9532012f7aecae49d459b29f6f6ad602061010083015101527f16ec03d260bd7ac1c50ffa63565d5274b4582ceea52ff40b81cdfe8f444f01e4610120820151527f29392152723097e07113c3d7786d245ec40c30928015cd50b5668a4f4ea17031602061012083015101527f2cdbfb3a4053c8489b0c94e74338ac19118df7a06bc56b1eb4d0e0dc4eae7248610140820151527f07fea127dae943b8dc148f1408d40cff465c9c4721943669b1e4fd5a39db7036602061014083015101527f031455a79a2e0ce78a6cb53526ec04ac19716a86b08a93df48d178f8b77e5619610160820151527f118623e6bc136ee6d3f9907cd4ad04a9418ea03ba99ad753227cdfee598e8415602061016083015101527f0861d1997761a852226aac7ba9717bf6ae56451099be774cdf02ef352a58cbc8610180820151527f0805e392bcbc12e40a722778632d73fe981e4bc6fa6d1178b70af7be1cb9a3a3602061018083015101527f101d1e3978cb9f1e303d413144ebe67682c9eb0cfe11242959aa6029d78cdbbc6101a0820151527f089eb9c727e6cb07082bc3e6f40cf04f439fe48000602b584774dad7efc6607c60206101a083015101527f2d489f2493263aa873bcd94f21efb45bf257a61d81c0c95c3297916506653b406101c0820151527f18e45d627aadd4df2794ecd9909fac1a753f0c6fa8a9c6654a7a58b0912fffd560206101c083015101527f0e43e3a4b13cb438e2ad924614261ad0240214fa1c83fcda6a0bf779eb39ffc56101e0820151527f0eaba9f429c5f6fc3103d4cc4056c500ff42425d8e6465c5b8e145219f9c5cd360206101e083015101527f29ae351d09dcf41c0a80ab05393738358baab37e6fbc464b3bb13258994a1fa4610200820151527f2b7bc74608d7ec7dadd0597d6a4010d8bfc2b31900281901cedc42bdbb0fb8fc602061020083015101527f066802c7ceb9e913d4f65433a20661e097acac1affecbb534a54f76a29782226610220820151527f27ec80e811e636f3348267923c8e641bd98a7e37c5216670cbff14ae323f9e0e602061022083015101527f12604d1f87c583f6c9710c73eaf590af9d07aa743d1381d0e9dff0eab2614239610240820151527f1588579e6c3378ea32cb641205ef762a63cd353a0bd670394528ad2081ee8dd4602061024083015101527f247d65261d3a4ab042ba937331f6d0c0c5eb9ea78753a92084db1a6939e19e82610260820151527f2ce6cc664a32147bfe6a0c94a95bf0496679405ccae01648cd4ec021145120d56020610260830151015290565b6000611fbd82612162565b611fe083600081518110611fd357611fd36159db565b6020026020010151610fd9565b611ff683600181518110611fd357611fd36159db565b61200c83600281518110611fd357611fd36159db565b61202283600381518110611fd357611fd36159db565b61203883600481518110611fd357611fd36159db565b61204e83600581518110611fd357611fd36159db565b61206483600681518110611fd357611fd36159db565b61207a83600781518110611fd357611fd36159db565b600061208785858561229a565b90506120928161241b565b9150505b9392505050565b6120a6826128e3565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156120eb576115d7828261298b565b610b92612a03565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16610c75576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f166120f3565b805161216d90612a3b565b61217a8160200151612a3b565b6121878160400151612a3b565b6121948160600151612a3b565b6121a18160800151612a3b565b6121ae8160a00151612a3b565b6121bb8160c00151612a3b565b6121c88160e00151612a3b565b6121d6816101000151612a3b565b6121e4816101200151612a3b565b6121f2816101400151612a3b565b612200816101600151612a3b565b61220e816101800151612a3b565b61221c816101a00151610fd9565b61222a816101c00151610fd9565b612238816101e00151610fd9565b612246816102000151610fd9565b612254816102200151610fd9565b612262816102400151610fd9565b612270816102600151610fd9565b61227e816102800151610fd9565b61228c816102a00151610fd9565b610f62816102c00151610fd9565b6122a26153e1565b83602001518351146122e0576040517f41f53b1200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006122ed858585612ae5565b905060006122fe8660000151612e16565b90506000612311828460a00151886131fa565b60408051601e8082526103e0820190925291925060009190602082016103c080368337505060408051601e8082526103e082019092529293506000929150602082015b6040805180820190915260008082526020820152815260200190600190039081612354579050509050600061238d8a858a89878761325a565b60a08701516060870151919250907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600081838509604080516101008101825260e09c8d0151815260208101969096528501525050506060810191909152608081019290925260a082015261016086015160c082015261018090950151928501929092525091949350505050565b6040805180820182526000808252602080830182905283518085018552828152908101829052835160028082526060820190955291937f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001939285918160200160208202803683375050604080516002808252606082019092529293506000929150602082015b60408051808201909152600080825260208201528152602001906001900390816124a157905050905060006001905080836000815181106124e4576124e46159db565b6020026020010181815250508760c0015182600081518110612508576125086159db565b602002602001018190525087600001518360018151811061252b5761252b6159db565b6020026020010181815250508760e001518260018151811061254f5761254f6159db565b6020026020010181905250612564828461328f565b6080890151519095506060935083925090506000612583826002615a0a565b61258e906001615a0a565b90508067ffffffffffffffff8111156125a9576125a96154a2565b6040519080825280602002602001820160405280156125d2578160200160208202803683370190505b5093508067ffffffffffffffff8111156125ee576125ee6154a2565b60405190808252806020026020018201604052801561263357816020015b604080518082019091526000808252602082015281526020019060019003908161260c5790505b509250505060008060005b8960800151518110156126d75789608001518181518110612661576126616159db565b602002602001015185838151811061267b5761267b6159db565b6020026020010181815250508960a00151818151811061269d5761269d6159db565b60200260200101518483815181106126b7576126b76159db565b60209081029190910101526126cd600183615a0a565b915060010161263e565b5088602001518482815181106126ef576126ef6159db565b6020026020010181815250508860c00151838281518110612712576127126159db565b6020908102919091010152612728600182615a0a565b895160408b01519192509060008982840990508087858151811061274e5761274e6159db565b6020026020010181815250505050508860e00151838281518110612774576127746159db565b602090810291909101015261278a600182615a0a565b60608a0151909150878184089250506127a282613389565b8482815181106127b4576127b46159db565b6020026020010181815250506127ec604080518082018252600080825260209182015281518083019092526001825260029082015290565b8382815181106127fe576127fe6159db565b602002602001018190525061281b612816848661328f565b6133df565b945050505050600060405180608001604052807f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081526020017f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181526020017f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e5581526020017f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe481525090506128d98382846128d461347e565b61354f565b9695505050505050565b806001600160a01b03163b600003612932576040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610942565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516129a89190615a1d565b600060405180830381855af49150503d80600081146129e3576040519150601f19603f3d011682016040523d82523d6000602084013e6129e8565b606091505b50915091506129f8858383613633565b925050505b92915050565b3415610c75576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160208201516000917f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47911590151615612a7557505050565b8251602084015182600384858586098509088382830914838210848410161693505050816115d75760405162461bcd60e51b815260206004820152601760248201527f426e3235343a20696e76616c696420473120706f696e740000000000000000006044820152606401610942565b612b2d60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805180820190915260608152600060208201527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001612b6e8287876136a8565b81518451612b7b90613a89565b612b888660200151613a89565b612b958760400151613a89565b612ba28860600151613a89565b612baf8960800151613a89565b604051602001612bc496959493929190615a39565b60408051601f198184030181529190528252612bdf82613b4c565b50612be982613b4c565b6060840152612bf782613b4c565b6080840152815160a0850151612c0c90613a89565b604051602001612c1d929190615ab8565b60408051601f198184030181529190528252612c3882613b4c565b8352815160c0850151612c4a90613a89565b612c578660e00151613a89565b612c65876101000151613a89565b612c73886101200151613a89565b612c81896101400151613a89565b604051602001612c9696959493929190615a39565b60408051601f198184030181529190528252612cb182613b4c565b60a084015281516101a0850151612cc790613bc0565b612cd5866101c00151613bc0565b612ce3876101e00151613bc0565b612cf1886102000151613bc0565b612cff896102200151613bc0565b604051602001612d1496959493929190615ae7565b60408051601f19818403018152919052808352610240850151612d3690613bc0565b612d44866102600151613bc0565b612d52876102800151613bc0565b612d60886102a00151613bc0565b612d6e896102c00151613bc0565b604051602001612d8396959493929190615ae7565b60408051601f198184030181529190528252612d9e82613b4c565b60c08401528151610160850151612db490613a89565b612dc2866101800151613a89565b604051602001612dd493929190615b23565b60408051601f198184030181529190528252612def82613b4c565b60e08401528251818180098282820960208601919091526040850152509195945050505050565b612e486040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b816201000003612edc57506040805160a0810182526010815260208101929092527f30641e0e92bebef818268d663bcad6dbcfd6c0149170f6d7d350b1b1fa6c1001908201527eeeb2cb5981ed45649abebde081dcff16c8601de4347e7dd1628ba2daac43b760608201527f0b5d56b77fe704e8e92338c0082f37e091126414c830e4c6922d5ac802d842d4608082015290565b816202000003612f7157506040805160a0810182526011815260208101929092527f30643640b9f82f90e83b698e5ea6179c7c05542e859533b48b9953a2f5360801908201527f1bf82deba7d74902c3708cc6e70e61f30512eca95655210e276e5858ce8f58e560608201527f244cf010c43ca87237d8b00bf9dd50c4c01c7f086bd4e8c920e75251d96f0d22608082015290565b81620400000361300657506040805160a0810182526012815260208101929092527f30644259cd94e7dd5045d7a27013b7fcd21c9e3b7fa75222e7bda49b729b0401908201527f19ddbcaf3a8d46c15c0176fbb5b95e4dc57088ff13f4d1bd84c6bfa57dcdc0e060608201527f036853f083780e87f8d7c71d111119c57dbe118c22d5ad707a82317466c5174c608082015290565b81620800000361309b57506040805160a0810182526013815260208101929092527f3064486657634403844b0eac78ca882cfd284341fcb0615a15cfcd17b14d8201908201527f2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd60608201527f06e402c0a314fb67a15cf806664ae1b722dbc0efe66e6c81d98f9924ca535321608082015290565b81621000000361313057506040805160a0810182526014815260208101929092527f30644b6c9c4a72169e4daa317d25f04512ae15c53b34e8f5acd8e155d0a6c101908201527f26125da10a0ed06327508aba06d1e303ac616632dbed349f53422da95333785760608201527f100c332d2100895fab6473bc2c51bfca521f45cb3baca6260852a8fde26c91f3608082015290565b816020036131c357506040805160a0810182526005815260208101929092527f2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001908201527f09c532c6306b93d29678200d47c0b2a99c18d51b838eeb1d3eed4c533bb512d060608201527f2724713603bfbd790aeaf3e7df25d8e7ef8f311334905b4d8c99980cf210979d608082015290565b6040517fe2ef09e500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b61321e60405180606001604052806000815260200160008152602001600081525090565b6132288484613d0c565b8082526132389085908590613d72565b6020820152805161324e90859084908690613df8565b60408201529392505050565b600080613268858789613fbf565b90506132788886898988886140bd565b6132838187866143db565b98975050505050505050565b604080518082019091526000808252602082015282518251146132f45760405162461bcd60e51b815260206004820181905260248201527f4d534d206572726f723a206c656e67746820646f6573206e6f74206d617463686044820152606401610942565b6133328360008151811061330a5761330a6159db565b602002602001015183600081518110613325576133256159db565b602002602001015161443d565b905060015b82518110156133825761337882613373868481518110613359576133596159db565b6020026020010151868581518110613325576133256159db565b6144e1565b9150600101613337565b5092915050565b60006133b57f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183615b7c565b6129fd907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001615b9e565b6040805180820190915260008082526020820152815160208301511590151615613407575090565b6040518060400160405280836000015181526020017f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47846020015161344c9190615b7c565b613476907f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47615b9e565b905292915050565b6134a96040518060800160405280600081526020016000815260200160008152602001600081525090565b60405180608001604052807f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed81526020017f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c281526020017f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa81526020017f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b815250905090565b60008060006040518751815260208801516020820152602087015160408201528651606082015260608701516080820152604087015160a0820152855160c0820152602086015160e08201526020850151610100820152845161012082015260608501516101408201526040850151610160820152602060006101808360085afa9150506000519150806136255760405162461bcd60e51b815260206004820152601c60248201527f426e3235343a2050616972696e6720636865636b206661696c656421000000006044820152606401610942565b50151590505b949350505050565b6060826136485761364382614588565b612096565b815115801561365f57506001600160a01b0384163b155b156136a1576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610942565b5080612096565b825160fe906136e36136b983613bc0565b6040516020016136cb91815260200190565b604051602081830303815290604052600060046145ca565b61371d6136f38660000151613bc0565b60405160200161370591815260200190565b604051602081830303815290604052600060086145ca565b61372d6136f38760200151613bc0565b6040516020016137409493929190615bb1565b60408051601f1981840301815291905280855261375d6001613bc0565b6137867f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a613bc0565b6137af7f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb025613bc0565b6137d87f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a613bc0565b6138017f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e881613bc0565b60405160200161381696959493929190615ae7565b60408051601f1981840301815291905280855260e084015161383790613a89565b613845856101000151613a89565b613853866101200151613a89565b613861876101400151613a89565b61386f886101600151613a89565b61387d896101800151613a89565b61388b8a6101e00151613a89565b6040516020016138a2989796959493929190615c08565b60408051601f198184030181529190528085526102008401516138c490613a89565b6138d2856102200151613a89565b6138e0866102400151613a89565b6138ee876101a00151613a89565b6138fc886101c00151613a89565b61390a896102600151613a89565b6040516020016139209796959493929190615cad565b60408051601f1981840301815291815281865284015161393f90613a89565b61394c8560600151613a89565b6139598660800151613a89565b6139668760a00151613a89565b6139738860c00151613a89565b60405160200161398896959493929190615a39565b60408051601f1981840301815291905280855282516139bf9084906000906139b2576139b26159db565b6020026020010151613bc0565b6139d5846001815181106139b2576139b26159db565b6139eb856002815181106139b2576139b26159db565b613a01866003815181106139b2576139b26159db565b613a17876004815181106139b2576139b26159db565b613a2d886005815181106139b2576139b26159db565b613a43896006815181106139b2576139b26159db565b613a598a6007815181106139b2576139b26159db565b604051602001613a7199989796959493929190615d3f565b60408051601f19818403018152919052909352505050565b805160208201516060916000911590151615613ac2577f4000000000000000000000000000000000000000000000000000000000000000175b60208301517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760019190911b10613b1657507f80000000000000000000000000000000000000000000000000000000000000005b8251613b23908217613bc0565b604051602001613b3591815260200190565b604051602081830303815290604052915050919050565b602080820151825180516040518381526000948594939291908101855b83811015613b835760208186018101518383015201613b69565b505060209182019020908601819052925060006128d97f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185615b7c565b60008190506008817eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff16901b6008827fff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0016901c1790506010817dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff16901b6010827fffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff000016901c1790506020817bffffffff00000000ffffffff00000000ffffffff00000000ffffffff16901b6020827fffffffff00000000ffffffff00000000ffffffff00000000ffffffff0000000016901c17905060408177ffffffffffffffff0000000000000000ffffffffffffffff16901b6040827fffffffffffffffff0000000000000000ffffffffffffffff000000000000000016901c179050608081901b608082901c179050919050565b81516000907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190838015613d625784935060005b82811015613d5657838586099450600101613d40565b50600184039350613d69565b6001830393505b50505092915050565b600082600103613d8457506001612096565b81600003613d9457506000612096565b60408401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190600090828186099050858015613dd657600187039250613ddd565b6001840392505b50613de7826146f2565b915082828209979650505050505050565b82516000907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190838303613e8b57600160005b82811015613e7e57818703613e5f57878181518110613e4c57613e4c6159db565b602002602001015194505050505061362b565b8380613e6d57613e6d615b66565b896060015183099150600101613e2b565b506000935050505061362b565b6000806000808a604001519050600080613ea58d886147aa565b905060008767ffffffffffffffff811115613ec257613ec26154a2565b604051908082528060200260200182016040528015613eeb578160200160208202803683370190505b509050888b850993506001925060005b88811015613f3057602081026020840101519550898d878c030896508987850960208281028401018890529350600101613efb565b50613f3a836146f2565b925060005b88811015613fad5760208102602084010151955089868609975089848909975060005b89811015613f8c57808214613f84576020810260208401015197508a888a0998505b600101613f62565b506020810260208f010151955089868909975089888c089a50600101613f3f565b50505050505050505050949350505050565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019050600083602001519050600084604001519050600060019050606088015160808901516101a08901516102408a0151878889838709858501088609945050506101c08901516102608a0151878889838709858501088609945050506101e08901516102808a0151878889838709858501088609945050506102008901516102a08a01518788898387098585010886099450505061022089015191506102c0890151868782898587080985099350505050875160208901518586868309870385089650508485838309860387089998505050505050505050565b6140cb86868686858761489b565b60c085015182517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019190819081908690601490811061410c5761410c6159db565b602002602001018181525050856000015184601481518110614130576141306159db565b60200260200101819052508282820990508085601581518110614155576141556159db565b602002602001018181525050856020015184601581518110614179576141796159db565b6020026020010181905250828282099050808560168151811061419e5761419e6159db565b6020026020010181815250508560400151846016815181106141c2576141c26159db565b602002602001018190525082828209905080856017815181106141e7576141e76159db565b60200260200101818152505085606001518460178151811061420b5761420b6159db565b60200260200101819052508282820990508085601881518110614230576142306159db565b602002602001018181525050856080015184601881518110614254576142546159db565b60200260200101819052508282820990508085601981518110614279576142796159db565b60200260200101818152505088604001518460198151811061429d5761429d6159db565b60200260200101819052508282820990508085601a815181106142c2576142c26159db565b602002602001018181525050886060015184601a815181106142e6576142e66159db565b60200260200101819052508282820990508085601b8151811061430b5761430b6159db565b602002602001018181525050886080015184601b8151811061432f5761432f6159db565b60200260200101819052508282820990508085601c81518110614354576143546159db565b6020026020010181815250508860a0015184601c81518110614378576143786159db565b60200260200101819052508282820990508760e0015185601d815181106143a1576143a16159db565b6020026020010181815250508560a0015184601d815181106143c5576143c56159db565b6020026020010181905250505050505050505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018381039060005b600a8110156144345760206015820102840151602082026101a0018601518384828409860894505050600101614403565b50509392505050565b6040805180820190915260008082526020820152614459615434565b835181526020808501519082015260408101839052600060608360808460076107d05a03fa9050808061448b57600080fd5b50806144d95760405162461bcd60e51b815260206004820152601960248201527f426e3235343a207363616c6172206d756c206661696c656421000000000000006044820152606401610942565b505092915050565b60408051808201909152600080825260208201526144fd615452565b8351815260208085015181830152835160408301528301516060808301919091526000908360c08460066107d05a03fa9050808061453a57600080fd5b50806144d95760405162461bcd60e51b815260206004820152601d60248201527f426e3235343a2067726f7570206164646974696f6e206661696c6564210000006044820152606401610942565b8051156145985780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060816145d881601f615a0a565b10156146265760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610942565b6146308284615a0a565b845110156146805760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610942565b60608215801561469f57604051915060008252602082016040526146e9565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156146d85780518352602092830192016146c0565b5050858452601f01601f1916604052505b50949350505050565b60008060007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905060405160208152602080820152602060408201528460608201526002820360808201528160a08201526020600060c08360055afa9250506000519250816147a35760405162461bcd60e51b815260206004820152601d60248201527f426e3235343a20706f7720707265636f6d70696c65206661696c6564210000006044820152606401610942565b5050919050565b606082602001518211156147ea576040517f8c5e11f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606083015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018467ffffffffffffffff81111561482b5761482b6154a2565b604051908082528060200260200182016040528015614854578160200160208202803683370190505b50935084151915613d695760208401856020028101600182526020820191505b8082101561489057828585099350838252602082019150614874565b505050505092915050565b6000806000806000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190508060208b015160208d01510995508a5193508060a08c015160608d0151099250806101a08a0151840891508060808c015183089150808483099350807f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a84099150806101c08a0151830891508060808c015183089150808483099350807f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb02584099150806101e08a0151830891508060808c015183089150808483099350807f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a84099150806102008a0151830891508060808c015183089150808483099350807f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e88184099150806102208a0151830891508060808c0151830891508084830993508084870895508860a0015188600081518110614a2457614a246159db565b60200260200101819052508587600081518110614a4357614a436159db565b6020026020010181815250508060608c01518c51099450806102c08a015186099450806102408a015160608d0151099250806101a08a0151840892508060808c015184089250808386099450806102608a015160608d0151099250806101c08a0151840892508060808c015184089250808386099450806102808a015160608d0151099250806101e08a0151840892508060808c015184089250808386099450806102a08a015160608d0151099250806102008a0151840892508060808c0151840892508083860994508b60c0015188600181518110614b2557614b256159db565b6020908102919091010152614b3a8582615b9e565b87600181518110614b4d57614b4d6159db565b602002602001018181525050886101a0015187600281518110614b7257614b726159db565b602002602001018181525050886101c0015187600381518110614b9757614b976159db565b602002602001018181525050886101e0015187600481518110614bbc57614bbc6159db565b60200260200101818152505088610200015187600581518110614be157614be16159db565b6020026020010181815250508b60e0015188600281518110614c0557614c056159db565b60200260200101819052508b610100015188600381518110614c2957614c296159db565b60200260200101819052508b610120015188600481518110614c4d57614c4d6159db565b60200260200101819052508b610140015188600581518110614c7157614c716159db565b6020026020010181905250806101c08a01516101a08b01510992508287600681518110614ca057614ca06159db565b6020026020010181815250508b610160015188600681518110614cc557614cc56159db565b6020026020010181905250806102008a01516101e08b01510992508287600781518110614cf457614cf46159db565b6020026020010181815250508b610180015188600781518110614d1957614d196159db565b60200260200101819052506101a089015192508083840991508082830991508082840992508287600881518110614d5257614d526159db565b6020026020010181815250508b6101e0015188600881518110614d7757614d776159db565b60200260200101819052506101c089015192508083840991508082830991508082840992508287600981518110614db057614db06159db565b6020026020010181815250508b610200015188600981518110614dd557614dd56159db565b60200260200101819052506101e089015192508083840991508082830991508082840992508287600a81518110614e0e57614e0e6159db565b6020026020010181815250508b610220015188600a81518110614e3357614e336159db565b602002602001018190525061020089015192508083840991508082830991508082840992508287600b81518110614e6c57614e6c6159db565b6020026020010181815250508b610240015188600b81518110614e9157614e916159db565b602002602001018190525088610220015181614ead9190615b9e565b87600c81518110614ec057614ec06159db565b6020026020010181815250508b6101a0015188600c81518110614ee557614ee56159db565b6020026020010181905250600187600d81518110614f0557614f056159db565b6020026020010181815250508b6101c0015188600d81518110614f2a57614f2a6159db565b6020026020010181905250806101c08a01516101a08b0151099250806101e08a015184099250806102008a015184099250806102208a0151840992508287600e81518110614f7a57614f7a6159db565b6020026020010181815250508b610260015188600e81518110614f9f57614f9f6159db565b60209081029190910101528951614fb69082615b9e565b87600f81518110614fc957614fc96159db565b6020026020010181815250508860c0015188600f81518110614fed57614fed6159db565b60200260200101819052508060018b510860a08c0151909350819080099150808284099250808360206010028901510991508187601081518110615033576150336159db565b6020026020010181815250508860e0015188601081518110615057576150576159db565b6020026020010181905250808360206011028901510991508187601181518110615083576150836159db565b602002602001018181525050886101000151886011815181106150a8576150a86159db565b60200260200101819052508083602060120289015109915081876012815181106150d4576150d46159db565b602002602001018181525050886101200151886012815181106150f9576150f96159db565b6020026020010181905250808360206013028901510991508187601381518110615125576151256159db565b6020026020010181815250508861014001518860138151811061514a5761514a6159db565b6020026020010181905250505050505050505050505050565b604051806102800160405280600081526020016000815260200161519a604051806040016040528060008152602001600081525090565b81526020016151bc604051806040016040528060008152602001600081525090565b81526020016151de604051806040016040528060008152602001600081525090565b8152602001615200604051806040016040528060008152602001600081525090565b8152602001615222604051806040016040528060008152602001600081525090565b8152602001615244604051806040016040528060008152602001600081525090565b8152602001615266604051806040016040528060008152602001600081525090565b8152602001615288604051806040016040528060008152602001600081525090565b81526020016152aa604051806040016040528060008152602001600081525090565b81526020016152cc604051806040016040528060008152602001600081525090565b81526020016152ee604051806040016040528060008152602001600081525090565b8152602001615310604051806040016040528060008152602001600081525090565b8152602001615332604051806040016040528060008152602001600081525090565b8152602001615354604051806040016040528060008152602001600081525090565b8152602001615376604051806040016040528060008152602001600081525090565b8152602001615398604051806040016040528060008152602001600081525090565b81526020016153ba604051806040016040528060008152602001600081525090565b81526020016153dc604051806040016040528060008152602001600081525090565b905290565b6040518061010001604052806000815260200160008152602001600081526020016000815260200160608152602001606081526020016153ba604051806040016040528060008152602001600081525090565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b80356001600160a01b03811681146131f557600080fd5b60006020828403121561549957600080fd5b61209682615470565b634e487b7160e01b600052604160045260246000fd5b6040516102e0810167ffffffffffffffff811182821017156154dc576154dc6154a2565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561550b5761550b6154a2565b604052919050565b803567ffffffffffffffff811681146131f557600080fd5b600061010080838503121561553f57600080fd5b6040519081019067ffffffffffffffff82118183101715615562576155626154a2565b8160405280925061557284615513565b815261558060208501615513565b602082015260408401356040820152606084013560608201526080840135608082015260a084013560a082015260c084013560c082015260e084013560e0820152505092915050565b6000604082840312156155db57600080fd5b6040516040810181811067ffffffffffffffff821117156155fe576155fe6154a2565b604052823581526020928301359281019290925250919050565b60008082840361058081121561562d57600080fd5b615637858561552b565b9250610100610480807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008401121561566e57600080fd5b6156766154b8565b9250615684878388016155c9565b8352610140615695888289016155c9565b60208501526101806156a989828a016155c9565b60408601526101c06156bd8a828b016155c9565b60608701526102006156d18b828c016155c9565b60808801526102406156e58c828d016155c9565b60a08901526102806156f98d828e016155c9565b60c08a01526102c061570d8e828f016155c9565b60e08b01526157208e6103008f016155c9565b898b01526157328e6103408f016155c9565b6101208b01526157468e6103808f016155c9565b878b01526157588e6103c08f016155c9565b6101608b015261576c8e6104008f016155c9565b868b01526104408d01356101a08b01526104608d0135858b0152878d01356101e08b01526104a08d0135848b01526104c08d01356102208b01526104e08d0135838b01526105008d01356102608b01526105208d0135828b01526105408d01356102a08b01526105608d0135818b0152505050505050505050809150509250929050565b6000806040838503121561580357600080fd5b61580c83615470565b915060208084013567ffffffffffffffff8082111561582a57600080fd5b818601915086601f83011261583e57600080fd5b813581811115615850576158506154a2565b61586284601f19601f840116016154e2565b9150808252878482850101111561587857600080fd5b80848401858401376000848284010152508093505050509250929050565b803563ffffffff811681146131f557600080fd5b6000602082840312156158bc57600080fd5b61209682615896565b600080600061014084860312156158db57600080fd5b6158e5858561552b565b92506158f46101008501615896565b91506159036101208501615470565b90509250925092565b6000610100828403121561591f57600080fd5b612096838361552b565b60005b8381101561594457818101518382015260200161592c565b50506000910152565b602081526000825180602084015261596c816040850160208701615929565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8181168382160280821691908281146144d9576144d9615980565b67ffffffffffffffff81811683821601908082111561338257613382615980565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615a0357600080fd5b5051919050565b808201808211156129fd576129fd615980565b60008251615a2f818460208701615929565b9190910192915050565b600087516020615a4c8285838d01615929565b885191840191615a5f8184848d01615929565b8851920191615a718184848c01615929565b8751920191615a838184848b01615929565b8651920191615a958184848a01615929565b8551920191615aa78184848901615929565b919091019998505050505050505050565b60008351615aca818460208801615929565b835190830190615ade818360208801615929565b01949350505050565b60008751615af9818460208c01615929565b9190910195865250602085019390935260408401919091526060830152608082015260a001919050565b60008451615b35818460208901615929565b845190830190615b49818360208901615929565b8451910190615b5c818360208801615929565b0195945050505050565b634e487b7160e01b600052601260045260246000fd5b600082615b9957634e487b7160e01b600052601260045260246000fd5b500690565b818103818111156129fd576129fd615980565b60008551615bc3818460208a01615929565b855190830190615bd7818360208a01615929565b8551910190615bea818360208901615929565b8451910190615bfd818360208801615929565b019695505050505050565b600089516020615c1b8285838f01615929565b8a5191840191615c2e8184848f01615929565b8a51920191615c408184848e01615929565b8951920191615c528184848d01615929565b8851920191615c648184848c01615929565b8751920191615c768184848b01615929565b8651920191615c888184848a01615929565b8551920191615c9a8184848901615929565b919091019b9a5050505050505050505050565b600088516020615cc08285838e01615929565b895191840191615cd38184848e01615929565b8951920191615ce58184848d01615929565b8851920191615cf78184848c01615929565b8751920191615d098184848b01615929565b8651920191615d1b8184848a01615929565b8551920191615d2d8184848901615929565b919091019a9950505050505050505050565b60008a51615d51818460208f01615929565b9190910198895250602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000191905056fea164736f6c6343000817000a" +"0x60a0604052306080523480156200001557600080fd5b506200002062000026565b620000da565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000775760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b608051615a4562000104600039600081816114320152818161145b01526115c70152615a456000f3fe6080604052600436106101525760003560e01c8063013fa5fc146101575780630d8e6e2c14610179578063313df7b1146101ab578063382b215a146101d8578063409939b7146101fc5780634847ae5d1461021c5780634f1ef2861461029e57806352d1902d146102b157806354646085146102c657806362827733146102db57806369cc6a04146102f15780637053fc5114610306578063715018a61461031b578063766718081461033057806376b6b7cb146103645780637f17baad1461037a57806382d07ff31461042d5780638584d23f146104425780638da5cb5b14610486578063a244d5961461049b578063a51e6fea146104bb578063aa922732146104db578063ad3cb1cc146104fb578063bd32519a14610539578063ca6fe8551461056a578063db13b60a14610580578063e0303301146105bf578063f0682054146105df578063f2fde38b14610611575b600080fd5b34801561016357600080fd5b50610177610172366004614f9d565b610631565b005b34801561018557600080fd5b506040805160018152600060208201819052918101919091526060015b60405180910390f35b3480156101b757600080fd5b506006546101cb906001600160a01b031681565b6040516101a29190614fb8565b3480156101e457600080fd5b506101ee60035481565b6040519081526020016101a2565b34801561020857600080fd5b5061017761021736600461513d565b6106f1565b34801561022857600080fd5b50610231610a1c565b6040516101a2919060006101008201905060018060401b038084511683528060208501511660208401525060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6101776102ac3660046152f7565b610aae565b3480156102bd57600080fd5b506101ee610acd565b3480156102d257600080fd5b506008546101ee565b3480156102e757600080fd5b506101ee60025481565b3480156102fd57600080fd5b50610177610aea565b34801561031257600080fd5b506007546101ee565b34801561032757600080fd5b50610177610b5a565b34801561033c57600080fd5b5060005461035790600160601b90046001600160401b031681565b6040516101a2919061539c565b34801561037057600080fd5b506101ee60015481565b34801561038657600080fd5b506103e76103953660046153c4565b600560208190526000918252604090912080546001820154600283015460038401546004850154958501546006909501546001600160401b0380861697600160401b9096041695939492939192919088565b604080516001600160401b03998a168152989097166020890152958701949094526060860192909252608085015260a084015260c083015260e0820152610100016101a2565b34801561043957600080fd5b50610231610b6c565b34801561044e57600080fd5b5061046261045d3660046153df565b610bfc565b6040805182516001600160401b0316815260209283015192810192909252016101a2565b34801561049257600080fd5b506101cb610d56565b3480156104a757600080fd5b506101776104b63660046153f8565b610d71565b3480156104c757600080fd5b506101ee6104d63660046153df565b610e9c565b3480156104e757600080fd5b506101ee6104f636600461543f565b610ebd565b34801561050757600080fd5b5061052c604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516101a29190615480565b34801561054557600080fd5b5060065461055a90600160a01b900460ff1681565b60405190151581526020016101a2565b34801561057657600080fd5b506101ee60045481565b34801561058c57600080fd5b506105a061059b3660046153df565b610f05565b604080516001600160401b0390931683526020830191909152016101a2565b3480156105cb57600080fd5b5061055a6105da3660046154b3565b610f3d565b3480156105eb57600080fd5b506000546105fc9063ffffffff1681565b60405163ffffffff90911681526020016101a2565b34801561061d57600080fd5b5061017761062c366004614f9d565b611020565b61063961105e565b6001600160a01b0381166106605760405163e6c4247b60e01b815260040160405180910390fd5b6006546001600160a01b039081169082160361068f5760405163a863aec960e01b815260040160405180910390fd5b600680546001600160a81b0319166001600160a01b0380841691909117600160a01b17918290556040517f8017bb887fdf8fca4314a9d40f6e73b3b81002d67e5cfa85d88173af6aa46072926106e6921690614fb8565b60405180910390a150565b600654600160a01b900460ff16801561071557506006546001600160a01b03163314155b1561075c576006546001600160a01b0316610743576040516312e6d1e760e11b815260040160405180910390fd5b6040516301474c8f60e71b815260040160405180910390fd5b610764610b6c565b5182516001600160401b0391821691161115806107a25750610784610b6c565b602001516001600160401b031682602001516001600160401b031611155b156107c05760405163051c46ef60e01b815260040160405180910390fd5b600080546107e59063ffffffff811690600160601b90046001600160401b03166154eb565b6000805463ffffffff600160401b91829004168252600560205260409091205491925090046001600160401b039081169082161480158161083b5750816001600160401b031684602001516001600160401b0316115b15610864578160405163036466bf60e31b815260040161085b919061539c565b60405180910390fd5b6108718460400151611090565b61087e8460600151611090565b61088b8460800151611090565b6108988460a00151611090565b6108a58460c00151611090565b80156108b3576108b36110ec565b6108bd8484611237565b60008054600160401b9081900463ffffffff168252600560208181526040808520895181548b850180516001600160401b039384166001600160801b0319909316929092179183169097021782558a8301805160018085019190915560608d015160028086019190915560808e0151600386015560a08e0151600486015560c08e01519785019790975560e08d01516006909401939093556007805480850182559089524360008051602061591983398151915290910155835180850185528751831681528151818701908152600880549586018155909952516000805160206159998339815191529390960292830180546001600160401b0319169683169690961790955595516000805160206159d983398151915290910155925188519251935193845284169391909116917fa04a773924505a418564363725f56832f5772e6b8d0dbd6efce724dfe803dae6910160405180910390a350505050565b610a24614d9b565b5060008054600160201b900463ffffffff1681526005602081815260409283902083516101008101855281546001600160401b038082168352600160401b90910416928101929092526001810154938201939093526002830154606082015260038301546080820152600483015460a08201529082015460c082015260069091015460e082015290565b610ab6611427565b610abf826114cc565b610ac98282611503565b5050565b6000610ad76115bc565b5060008051602061595983398151915290565b610af261105e565b600654600160a01b900460ff1615610b3f57600680546001600160a81b03191690556040517f9a5f57de856dd668c54dd95e5c55df93432171cbca49a8776d5620ea59c0245090600090a1565b60405163a863aec960e01b815260040160405180910390fd5b565b610b6261105e565b610b586000611605565b610b74614d9b565b506000805463ffffffff600160401b918290041682526005602081815260409384902084516101008101865281546001600160401b038082168352959004909416918401919091526001810154938301939093526002830154606083015260038301546080830152600483015460a083015282015460c082015260069091015460e082015290565b60408051808201909152600080825260208201526008805490610c2060018361550e565b81548110610c3057610c30615521565b60009182526020909120600290910201546001600160401b03168310610c6957604051631856a49960e21b815260040160405180910390fd5b60005b81811015610cfe578360088281548110610c8857610c88615521565b60009182526020909120600290910201546001600160401b03161115610cf65760088181548110610cbb57610cbb615521565b60009182526020918290206040805180820190915260029092020180546001600160401b031682526001015491810191909152949350505050565b600101610c6c565b506008610d0c60018361550e565b81548110610d1c57610d1c615521565b60009182526020918290206040805180820190915260029092020180546001600160401b0316825260010154918101919091529392505050565b600080610d61611661565b546001600160a01b031692915050565b6000610d7b611685565b805490915060ff600160401b82041615906001600160401b0316600081158015610da25750825b90506000826001600160401b03166001148015610dbe5750303b155b905081158015610dcc575080155b15610dea5760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315610e1357845460ff60401b1916600160401b1785555b610e1c866116a9565b610e246116ba565b60008054600160201b600160601b031916600160401b179055610e4788886116c2565b8315610e9257845460ff60401b191685556040517fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290610e899060019061539c565b60405180910390a15b5050505050505050565b60078181548110610eac57600080fd5b600091825260209091200154905081565b60808082015160a083015160c0840151604080516020810194909452830191909152606082015260009101604051602081830303815290604052805190602001209050919050565b60088181548110610f1557600080fd5b6000918252602090912060029091020180546001909101546001600160401b03909116915082565b60075460009043841180610f515750600381105b15610f6f5760405163b0b4387760e01b815260040160405180910390fd5b60008080610f7e60018561550e565b90505b81610fe9578660078281548110610f9a57610f9a615521565b906000526020600020015411610fcf576001915060078181548110610fc157610fc1615521565b906000526020600020015492505b60028110610fe95780610fe181615537565b915050610f81565b816110075760405163b0b4387760e01b815260040160405180910390fd5b85611012848961550e565b119450505050505b92915050565b61102861105e565b6001600160a01b038116611052576000604051631e4fbdf760e01b815260040161085b9190614fb8565b61105b81611605565b50565b33611067610d56565b6001600160a01b031614610b58573360405163118cdaa760e01b815260040161085b9190614fb8565b600080516020615979833981519152811080610ac95760405162461bcd60e51b815260206004820152601b60248201527a109b8c8d4d0e881a5b9d985b1a59081cd8d85b185c88199a595b19602a1b604482015260640161085b565b6000805463ffffffff600160401b9182900416825260056020818152604080852081516101008101835281546001600160401b038082168352969004909516928501929092526001820154908401526002810154606084015260038101546080840152600481015460a08401529081015460c08301526006015460e082015261117490610ebd565b600380546001908155908290556004805460025560008054600160401b810463ffffffff16825260056020526040822060060154909255929350909190600c906111cf908490600160601b90046001600160401b031661554e565b92506101000a8154816001600160401b0302191690836001600160401b031602179055507fdb3558259e039d7e50e816b9dcce30fb114d8a9c86eca5ab14b60194d6945d3f6000600c9054906101000a90046001600160401b03166040516106e6919061539c565b60006112416119e5565b60408051600880825261012082019092529192506000919060208201610100803683370190505090506002548160008151811061128057611280615521565b60200260200101818152505083600001516001600160401b0316816001815181106112ad576112ad615521565b60200260200101818152505083602001516001600160401b0316816002815181106112da576112da615521565b6020026020010181815250508360400151816003815181106112fe576112fe615521565b60200260200101818152505083606001518160048151811061132257611322615521565b60209081029190910181019190915260008054600160401b900463ffffffff1681526005918290526040902060030154825190918391811061136657611366615521565b60209081029190910181019190915260008054600160401b900463ffffffff168152600590915260409020600401548151829060069081106113aa576113aa615521565b60209081029190910181019190915260008054600160401b900463ffffffff1681526005918290526040902001548151829060079081106113ed576113ed615521565b602002602001018181525050611404828285611fc7565b611421576040516309bde33960e01b815260040160405180910390fd5b50505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806114ae57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166114a2600080516020615959833981519152546001600160a01b031690565b6001600160a01b031614155b15610b585760405163703e46dd60e11b815260040160405180910390fd5b6114d461105e565b7ff78721226efe9a1bb678189a16d1554928b9f2192e2cb93eeda83b79fa40007d816040516106e69190614fb8565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561155d575060408051601f3d908101601f1916820190925261155a9181019061556e565b60015b61157c5781604051634c9c8ce360e01b815260040161085b9190614fb8565b60008051602061595983398151915281146115ad57604051632a87526960e21b81526004810182905260240161085b565b6115b783836120b2565b505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610b585760405163703e46dd60e11b815260040160405180910390fd5b600061160f611661565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b6116b1612108565b61105b8161212d565b610b58612108565b81516001600160401b03161515806116e6575060208201516001600160401b031615155b806116f357506080820151155b80611700575060a0820151155b8061170d575060c0820151155b8061171a575060e0820151155b80611729575063ffffffff8116155b15611747576040516350dd03f760e11b815260040160405180910390fd5b81600560008060049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e0820151816006015590505081600560008060089054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e08201518160060155905050600080600c6101000a8154816001600160401b0302191690836001600160401b03160217905550806000806101000a81548163ffffffff021916908363ffffffff160217905550600061192d83610ebd565b600181815560e08501516002818155600393909355600455600780548083018255600091825243600080516020615919833981519152909101556040805180820182526020808901516001600160401b039081168352929098015197810197885260088054948501815590925290516000805160206159998339815191529290930291820180546001600160401b031916939091169290921790915592516000805160206159d9833981519152909301929092555050565b6119ed614df2565b621000008152600860208201527f20c9403133dfde9a9d382df76fb0523571648725abc0a7c12830bb690ec83b336040820151527f03a0a9acc3e3815a7ed6cb1379f7d157e6343164729376392a693acbd3ec283c6020604083015101527f2866c18ad1df10ef13542cce6250ce02cb2a6b72ae00a9852e271187e9e4e0db6060820151527f21be232a42246a5663ebf483470cca666ffe9d4f0e63b929c596a7658714e9706020606083015101527f07d77873b9860074118e75808c79468b83c8ed64ba14db5cb5afa8e534de7b996080820151527f0be0f448839080132d47de17de0099b4cd74ae1e6b71cdda06cdebb868a50c6d6020608083015101527f13bd45a023491eadea44cc3f24cfbd1796eade9c0e39ee81d9f63ea0a580662560a0820151527f18f95cdda42ce11d9d10a3b335acc214e3807c578c5359405d810c208df60093602060a083015101527f0970d978763461f09e9ec63454073497386e4d282fedc2ac5b967cb9fd3fa8a960c0820151527f28c2217f7bacf6f8b2b8ee4a90fcf8b5bca04205ea84e8e1eb54b85dd41bde28602060c083015101527f02fe3d02988db718380052970aba46a3296df5f29b736ba1f2c4ccffc8b5969360e0820151527f202c3e390cee7c5c8525da2329a19f4936f6f71ca97dde6c6fa32b382d5acc03602060e083015101527f23ac10ae6ca5cacee8744bb939aaa835390954b91ae668a2c8d0edda558a89e7610100820151527f1c8c2b856cdade256ba3237f39afd5e170a9532012f7aecae49d459b29f6f6ad602061010083015101527f16ec03d260bd7ac1c50ffa63565d5274b4582ceea52ff40b81cdfe8f444f01e4610120820151527f29392152723097e07113c3d7786d245ec40c30928015cd50b5668a4f4ea17031602061012083015101527f2cdbfb3a4053c8489b0c94e74338ac19118df7a06bc56b1eb4d0e0dc4eae7248610140820151527f07fea127dae943b8dc148f1408d40cff465c9c4721943669b1e4fd5a39db7036602061014083015101527f031455a79a2e0ce78a6cb53526ec04ac19716a86b08a93df48d178f8b77e5619610160820151527f118623e6bc136ee6d3f9907cd4ad04a9418ea03ba99ad753227cdfee598e8415602061016083015101527f0861d1997761a852226aac7ba9717bf6ae56451099be774cdf02ef352a58cbc8610180820151527f0805e392bcbc12e40a722778632d73fe981e4bc6fa6d1178b70af7be1cb9a3a3602061018083015101527f101d1e3978cb9f1e303d413144ebe67682c9eb0cfe11242959aa6029d78cdbbc6101a0820151527f089eb9c727e6cb07082bc3e6f40cf04f439fe48000602b584774dad7efc6607c60206101a083015101527f2d489f2493263aa873bcd94f21efb45bf257a61d81c0c95c3297916506653b406101c0820151527f18e45d627aadd4df2794ecd9909fac1a753f0c6fa8a9c6654a7a58b0912fffd560206101c083015101527f0e43e3a4b13cb438e2ad924614261ad0240214fa1c83fcda6a0bf779eb39ffc56101e0820151527f0eaba9f429c5f6fc3103d4cc4056c500ff42425d8e6465c5b8e145219f9c5cd360206101e083015101527f29ae351d09dcf41c0a80ab05393738358baab37e6fbc464b3bb13258994a1fa4610200820151527f2b7bc74608d7ec7dadd0597d6a4010d8bfc2b31900281901cedc42bdbb0fb8fc602061020083015101527f066802c7ceb9e913d4f65433a20661e097acac1affecbb534a54f76a29782226610220820151527f27ec80e811e636f3348267923c8e641bd98a7e37c5216670cbff14ae323f9e0e602061022083015101527f12604d1f87c583f6c9710c73eaf590af9d07aa743d1381d0e9dff0eab2614239610240820151527f1588579e6c3378ea32cb641205ef762a63cd353a0bd670394528ad2081ee8dd4602061024083015101527f247d65261d3a4ab042ba937331f6d0c0c5eb9ea78753a92084db1a6939e19e82610260820151527f2ce6cc664a32147bfe6a0c94a95bf0496679405ccae01648cd4ec021145120d56020610260830151015290565b6000611fd282612135565b611ff583600081518110611fe857611fe8615521565b6020026020010151611090565b61200b83600181518110611fe857611fe8615521565b61202183600281518110611fe857611fe8615521565b61203783600381518110611fe857611fe8615521565b61204d83600481518110611fe857611fe8615521565b61206383600581518110611fe857611fe8615521565b61207983600681518110611fe857611fe8615521565b61208f83600781518110611fe857611fe8615521565b600061209c85858561226d565b90506120a7816123b7565b9150505b9392505050565b6120bb82612816565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115612100576115b78282612872565b610ac96128e8565b612110612907565b610b5857604051631afcd79f60e31b815260040160405180910390fd5b611028612108565b805161214090612921565b61214d8160200151612921565b61215a8160400151612921565b6121678160600151612921565b6121748160800151612921565b6121818160a00151612921565b61218e8160c00151612921565b61219b8160e00151612921565b6121a9816101000151612921565b6121b7816101200151612921565b6121c5816101400151612921565b6121d3816101600151612921565b6121e1816101800151612921565b6121ef816101a00151611090565b6121fd816101c00151611090565b61220b816101e00151611090565b612219816102000151611090565b612227816102200151611090565b612235816102400151611090565b612243816102600151611090565b612251816102800151611090565b61225f816102a00151611090565b61105b816102c00151611090565b612275614ef6565b836020015183511461229a576040516320fa9d8960e11b815260040160405180910390fd5b60006122a78585856129af565b905060006122b88660000151612cce565b905060006122cb828460a0015188613099565b60408051601e8082526103e0820190925291925060009190602082016103c080368337505060408051601e8082526103e082019092529293506000929150602082015b612316614f30565b81526020019060019003908161230e579050509050600061233b8a858a8987876130f9565b60a0870151606087015191925090600080516020615979833981519152600081838509604080516101008101825260e09c8d0151815260208101969096528501525050506060810191909152608081019290925260a082015261016086015160c082015261018090950151928501929092525091949350505050565b60006000805160206159798339815191526123d0614f30565b6123d8614f30565b6040805160028082526060820183526000926020830190803683375050604080516002808252606082019092529293506000929150602082015b61241a614f30565b815260200190600190039081612412579050509050600060019050808360008151811061244957612449615521565b6020026020010181815250508760c001518260008151811061246d5761246d615521565b602002602001018190525087600001518360018151811061249057612490615521565b6020026020010181815250508760e00151826001815181106124b4576124b4615521565b60200260200101819052506124c9828461312e565b60808901515190955060609350839250905060006124e8826002615587565b6124f3906001615587565b9050806001600160401b0381111561250d5761250d614fcc565b604051908082528060200260200182016040528015612536578160200160208202803683370190505b509350806001600160401b0381111561255157612551614fcc565b60405190808252806020026020018201604052801561258a57816020015b612577614f30565b81526020019060019003908161256f5790505b509250505060008060005b89608001515181101561262e57896080015181815181106125b8576125b8615521565b60200260200101518583815181106125d2576125d2615521565b6020026020010181815250508960a0015181815181106125f4576125f4615521565b602002602001015184838151811061260e5761260e615521565b6020908102919091010152612624600183615587565b9150600101612595565b50886020015184828151811061264657612646615521565b6020026020010181815250508860c0015183828151811061266957612669615521565b602090810291909101015261267f600182615587565b895160408b0151919250906000898284099050808785815181106126a5576126a5615521565b6020026020010181815250505050508860e001518382815181106126cb576126cb615521565b60209081029190910101526126e1600182615587565b60608a0151909150878184089250506126f98261321c565b84828151811061270b5761270b615521565b60200260200101818152505061271f61324e565b83828151811061273157612731615521565b602002602001018190525061274e612749848661312e565b61326f565b945050505050600060405180608001604052807f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081526020017f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181526020017f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e5581526020017f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4815250905061280c8382846128076132dc565b6133ad565b9695505050505050565b806001600160a01b03163b6000036128435780604051634c9c8ce360e01b815260040161085b9190614fb8565b60008051602061595983398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b03168460405161288f919061559a565b600060405180830381855af49150503d80600081146128ca576040519150601f19603f3d011682016040523d82523d6000602084013e6128cf565b606091505b50915091506128df858383613490565b95945050505050565b3415610b585760405163b398979f60e01b815260040160405180910390fd5b6000612911611685565b54600160401b900460ff16919050565b60006000805160206158f983398151915261293b836134e3565b1561294557505050565b8251602084015182600384858586098509088382830914838210848410161693505050816115b75760405162461bcd60e51b8152602060048201526017602482015276109b8c8d4d0e881a5b9d985b1a590811cc481c1bda5b9d604a1b604482015260640161085b565b6129f760405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604080518082019091526060815260006020820152600080516020615979833981519152612a268287876134f2565b81518451612a339061388b565b612a40866020015161388b565b612a4d876040015161388b565b612a5a886060015161388b565b612a67896080015161388b565b604051602001612a7c969594939291906155b6565b60408051601f198184030181529190528252612a9782613900565b50612aa182613900565b6060840152612aaf82613900565b6080840152815160a0850151612ac49061388b565b604051602001612ad5929190615635565b60408051601f198184030181529190528252612af082613900565b8352815160c0850151612b029061388b565b612b0f8660e0015161388b565b612b1d87610100015161388b565b612b2b88610120015161388b565b612b3989610140015161388b565b604051602001612b4e969594939291906155b6565b60408051601f198184030181529190528252612b6982613900565b60a084015281516101a0850151612b7f90613962565b612b8d866101c00151613962565b612b9b876101e00151613962565b612ba9886102000151613962565b612bb7896102200151613962565b604051602001612bcc96959493929190615664565b60408051601f19818403018152919052808352610240850151612bee90613962565b612bfc866102600151613962565b612c0a876102800151613962565b612c18886102a00151613962565b612c26896102c00151613962565b604051602001612c3b96959493929190615664565b60408051601f198184030181529190528252612c5682613900565b60c08401528151610160850151612c6c9061388b565b612c7a86610180015161388b565b604051602001612c8c939291906156a0565b60408051601f198184030181529190528252612ca782613900565b60e08401528251818180098282820960208601919091526040850152509195945050505050565b612d006040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b816201000003612d9457506040805160a0810182526010815260208101929092527f30641e0e92bebef818268d663bcad6dbcfd6c0149170f6d7d350b1b1fa6c1001908201527eeeb2cb5981ed45649abebde081dcff16c8601de4347e7dd1628ba2daac43b760608201527f0b5d56b77fe704e8e92338c0082f37e091126414c830e4c6922d5ac802d842d4608082015290565b816202000003612e2957506040805160a0810182526011815260208101929092527f30643640b9f82f90e83b698e5ea6179c7c05542e859533b48b9953a2f5360801908201527f1bf82deba7d74902c3708cc6e70e61f30512eca95655210e276e5858ce8f58e560608201527f244cf010c43ca87237d8b00bf9dd50c4c01c7f086bd4e8c920e75251d96f0d22608082015290565b816204000003612ebe57506040805160a0810182526012815260208101929092527f30644259cd94e7dd5045d7a27013b7fcd21c9e3b7fa75222e7bda49b729b0401908201527f19ddbcaf3a8d46c15c0176fbb5b95e4dc57088ff13f4d1bd84c6bfa57dcdc0e060608201527f036853f083780e87f8d7c71d111119c57dbe118c22d5ad707a82317466c5174c608082015290565b816208000003612f5357506040805160a0810182526013815260208101929092527f3064486657634403844b0eac78ca882cfd284341fcb0615a15cfcd17b14d8201908201527f2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd60608201527f06e402c0a314fb67a15cf806664ae1b722dbc0efe66e6c81d98f9924ca535321608082015290565b816210000003612fe857506040805160a0810182526014815260208101929092527f30644b6c9c4a72169e4daa317d25f04512ae15c53b34e8f5acd8e155d0a6c101908201527f26125da10a0ed06327508aba06d1e303ac616632dbed349f53422da95333785760608201527f100c332d2100895fab6473bc2c51bfca521f45cb3baca6260852a8fde26c91f3608082015290565b8160200361307b57506040805160a0810182526005815260208101929092527f2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001908201527f09c532c6306b93d29678200d47c0b2a99c18d51b838eeb1d3eed4c533bb512d060608201527f2724713603bfbd790aeaf3e7df25d8e7ef8f311334905b4d8c99980cf210979d608082015290565b60405163e2ef09e560e01b815260040160405180910390fd5b919050565b6130bd60405180606001604052806000815260200160008152602001600081525090565b6130c78484613a99565b8082526130d79085908590613aed565b602082015280516130ed90859084908690613b61565b60408201529392505050565b600080613107858789613d15565b9050613117888689898888613e01565b61312281878661410d565b98975050505050505050565b613136614f30565b82518251146131875760405162461bcd60e51b815260206004820181905260248201527f4d534d206572726f723a206c656e67746820646f6573206e6f74206d61746368604482015260640161085b565b6131c58360008151811061319d5761319d615521565b6020026020010151836000815181106131b8576131b8615521565b602002602001015161415d565b905060015b82518110156132155761320b826132068684815181106131ec576131ec615521565b60200260200101518685815181106131b8576131b8615521565b6141f1565b91506001016131ca565b5092915050565b6000613236600080516020615979833981519152836156f9565b61101a9060008051602061597983398151915261550e565b613256614f30565b5060408051808201909152600181526002602082015290565b613277614f30565b613280826134e3565b15613289575090565b6040518060400160405280836000015181526020016000805160206158f983398151915284602001516132bc91906156f9565b6132d4906000805160206158f983398151915261550e565b905292915050565b6133076040518060800160405280600081526020016000815260200160008152602001600081525090565b60405180608001604052807f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed81526020017f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c281526020017f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa81526020017f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b815250905090565b60008060006040518751815260208801516020820152602087015160408201528651606082015260608701516080820152604087015160a0820152855160c0820152602086015160e08201526020850151610100820152845161012082015260608501516101408201526040850151610160820152602060006101808360085afa9150506000519150806134825760405162461bcd60e51b815260206004820152601c60248201527b426e3235343a2050616972696e6720636865636b206661696c65642160201b604482015260640161085b565b50151590505b949350505050565b6060826134a5576134a08261428c565b6120ab565b81511580156134bc57506001600160a01b0384163b155b156134dc5783604051639996b31560e01b815260040161085b9190614fb8565b50806120ab565b80516020909101511590151690565b825160fe9061352d61350383613962565b60405160200161351591815260200190565b604051602081830303815290604052600060046142b5565b61356761353d8660000151613962565b60405160200161354f91815260200190565b604051602081830303815290604052600060086142b5565b61357761353d8760200151613962565b60405160200161358a949392919061571b565b60408051601f198184030181529190528085526135a76001613962565b6135be600080516020615a19833981519152613962565b6135d56000805160206159b9833981519152613962565b6135ec6000805160206159f9833981519152613962565b613603600080516020615939833981519152613962565b60405160200161361896959493929190615664565b60408051601f1981840301815291905280855260e08401516136399061388b565b61364785610100015161388b565b61365586610120015161388b565b61366387610140015161388b565b61367188610160015161388b565b61367f89610180015161388b565b61368d8a6101e0015161388b565b6040516020016136a4989796959493929190615772565b60408051601f198184030181529190528085526102008401516136c69061388b565b6136d485610220015161388b565b6136e286610240015161388b565b6136f0876101a0015161388b565b6136fe886101c0015161388b565b61370c89610260015161388b565b6040516020016137229796959493929190615817565b60408051601f198184030181529181528186528401516137419061388b565b61374e856060015161388b565b61375b866080015161388b565b6137688760a0015161388b565b6137758860c0015161388b565b60405160200161378a969594939291906155b6565b60408051601f1981840301815291905280855282516137c19084906000906137b4576137b4615521565b6020026020010151613962565b6137d7846001815181106137b4576137b4615521565b6137ed856002815181106137b4576137b4615521565b613803866003815181106137b4576137b4615521565b613819876004815181106137b4576137b4615521565b61382f886005815181106137b4576137b4615521565b613845896006815181106137b4576137b4615521565b61385b8a6007815181106137b4576137b4615521565b604051602001613873999897969594939291906158a9565b60408051601f19818403018152919052909352505050565b60606000613898836134e3565b156138a457600160fe1b175b60208301516000805160206158f983398151915260019190911b106138ca5750600160ff1b5b82516138d7908217613962565b6040516020016138e991815260200190565b604051602081830303815290604052915050919050565b602080820151825180516040518381526000948594939291908101855b83811015613937576020818601810151838301520161391d565b5050602091820190209086018190529250600061280c600080516020615979833981519152856156f9565b60008190506008817eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff16901b6008827fff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0016901c1790506010817dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff16901b6010827dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff1916901c1790506020817bffffffff00000000ffffffff00000000ffffffff00000000ffffffff16901b6020827bffffffff00000000ffffffff00000000ffffffff00000000ffffffff1916901c1790506040816001600160401b03600160801b03600160c01b0316901b6040826001600160401b03600160801b03600160c01b031916901c179050608081901b608082901c179050919050565b815160009060008051602061597983398151915290838015613add5784935060005b82811015613ad157838586099450600101613abb565b50600184039350613ae4565b6001830393505b50505092915050565b600082600103613aff575060016120ab565b81600003613b0f575060006120ab565b604084015160008051602061597983398151915290600090828186099050858015613b3f57600187039250613b46565b6001840392505b50613b50826143c2565b915082828209979650505050505050565b825160009060008051602061597983398151915290838303613be257600160005b82811015613bd557818703613bb657878181518110613ba357613ba3615521565b6020026020010151945050505050613488565b8380613bc457613bc46156e3565b896060015183099150600101613b82565b5060009350505050613488565b6000806000808a604001519050600080613bfc8d88614468565b90506000876001600160401b03811115613c1857613c18614fcc565b604051908082528060200260200182016040528015613c41578160200160208202803683370190505b509050888b850993506001925060005b88811015613c8657602081026020840101519550898d878c030896508987850960208281028401018890529350600101613c51565b50613c90836143c2565b925060005b88811015613d035760208102602084010151955089868609975089848909975060005b89811015613ce257808214613cda576020810260208401015197508a888a0998505b600101613cb8565b506020810260208f010151955089868909975089888c089a50600101613c95565b50505050505050505050949350505050565b6000806000805160206159798339815191529050600083602001519050600084604001519050600060019050606088015160808901516101a08901516102408a0151878889838709858501088609945050506101c08901516102608a0151878889838709858501088609945050506101e08901516102808a0151878889838709858501088609945050506102008901516102a08a01518788898387098585010886099450505061022089015191506102c0890151868782898587080985099350505050875160208901518586868309870385089650508485838309860387089998505050505050505050565b613e0f86868686858761452d565b60c0850151825160008051602061597983398151915291908190819086906014908110613e3e57613e3e615521565b602002602001018181525050856000015184601481518110613e6257613e62615521565b60200260200101819052508282820990508085601581518110613e8757613e87615521565b602002602001018181525050856020015184601581518110613eab57613eab615521565b60200260200101819052508282820990508085601681518110613ed057613ed0615521565b602002602001018181525050856040015184601681518110613ef457613ef4615521565b60200260200101819052508282820990508085601781518110613f1957613f19615521565b602002602001018181525050856060015184601781518110613f3d57613f3d615521565b60200260200101819052508282820990508085601881518110613f6257613f62615521565b602002602001018181525050856080015184601881518110613f8657613f86615521565b60200260200101819052508282820990508085601981518110613fab57613fab615521565b602002602001018181525050886040015184601981518110613fcf57613fcf615521565b60200260200101819052508282820990508085601a81518110613ff457613ff4615521565b602002602001018181525050886060015184601a8151811061401857614018615521565b60200260200101819052508282820990508085601b8151811061403d5761403d615521565b602002602001018181525050886080015184601b8151811061406157614061615521565b60200260200101819052508282820990508085601c8151811061408657614086615521565b6020026020010181815250508860a0015184601c815181106140aa576140aa615521565b60200260200101819052508282820990508760e0015185601d815181106140d3576140d3615521565b6020026020010181815250508560a0015184601d815181106140f7576140f7615521565b6020026020010181905250505050505050505050565b6000805160206159798339815191528381039060005b600a8110156141545760206015820102840151602082026101a0018601518384828409860894505050600101614123565b50509392505050565b614165614f30565b61416d614f4a565b835181526020808501519082015260408101839052600060608360808460076107d05a03fa9050808061419f57600080fd5b50806141e95760405162461bcd60e51b8152602060048201526019602482015278426e3235343a207363616c6172206d756c206661696c65642160381b604482015260640161085b565b505092915050565b6141f9614f30565b614201614f68565b8351815260208085015181830152835160408301528301516060808301919091526000908360c08460066107d05a03fa9050808061423e57600080fd5b50806141e95760405162461bcd60e51b815260206004820152601d60248201527f426e3235343a2067726f7570206164646974696f6e206661696c656421000000604482015260640161085b565b80511561429c5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6060816142c381601f615587565b10156143025760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b604482015260640161085b565b61430c8284615587565b845110156143505760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b604482015260640161085b565b60608215801561436f57604051915060008252602082016040526143b9565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156143a8578051835260209283019201614390565b5050858452601f01601f1916604052505b50949350505050565b6000806000600080516020615979833981519152905060405160208152602080820152602060408201528460608201526002820360808201528160a08201526020600060c08360055afa9250506000519250816144615760405162461bcd60e51b815260206004820152601d60248201527f426e3235343a20706f7720707265636f6d70696c65206661696c656421000000604482015260640161085b565b5050919050565b6060826020015182111561448f57604051638c5e11f160e01b815260040160405180910390fd5b60608301516001600080516020615979833981519152846001600160401b038111156144bd576144bd614fcc565b6040519080825280602002602001820160405280156144e6578160200160208202803683370190505b50935084151915613ae45760208401856020028101600182526020820191505b8082101561452257828585099350838252602082019150614506565b505050505092915050565b60008060008060008060008051602061597983398151915290508060208b015160208d01510995508a5193508060a08c015160608d0151099250806101a08a0151840891508060808c01518308915080848309935080600080516020615a1983398151915284099150806101c08a0151830891508060808c015183089150808483099350806000805160206159b983398151915284099150806101e08a0151830891508060808c015183089150808483099350806000805160206159f983398151915284099150806102008a0151830891508060808c0151830891508084830993508060008051602061593983398151915284099150806102208a0151830891508060808c0151830891508084830993508084870895508860a001518860008151811061465c5761465c615521565b6020026020010181905250858760008151811061467b5761467b615521565b6020026020010181815250508060608c01518c51099450806102c08a015186099450806102408a015160608d0151099250806101a08a0151840892508060808c015184089250808386099450806102608a015160608d0151099250806101c08a0151840892508060808c015184089250808386099450806102808a015160608d0151099250806101e08a0151840892508060808c015184089250808386099450806102a08a015160608d0151099250806102008a0151840892508060808c0151840892508083860994508b60c001518860018151811061475d5761475d615521565b6020908102919091010152614772858261550e565b8760018151811061478557614785615521565b602002602001018181525050886101a00151876002815181106147aa576147aa615521565b602002602001018181525050886101c00151876003815181106147cf576147cf615521565b602002602001018181525050886101e00151876004815181106147f4576147f4615521565b6020026020010181815250508861020001518760058151811061481957614819615521565b6020026020010181815250508b60e001518860028151811061483d5761483d615521565b60200260200101819052508b61010001518860038151811061486157614861615521565b60200260200101819052508b61012001518860048151811061488557614885615521565b60200260200101819052508b6101400151886005815181106148a9576148a9615521565b6020026020010181905250806101c08a01516101a08b015109925082876006815181106148d8576148d8615521565b6020026020010181815250508b6101600151886006815181106148fd576148fd615521565b6020026020010181905250806102008a01516101e08b0151099250828760078151811061492c5761492c615521565b6020026020010181815250508b61018001518860078151811061495157614951615521565b60200260200101819052506101a08901519250808384099150808283099150808284099250828760088151811061498a5761498a615521565b6020026020010181815250508b6101e00151886008815181106149af576149af615521565b60200260200101819052506101c0890151925080838409915080828309915080828409925082876009815181106149e8576149e8615521565b6020026020010181815250508b610200015188600981518110614a0d57614a0d615521565b60200260200101819052506101e089015192508083840991508082830991508082840992508287600a81518110614a4657614a46615521565b6020026020010181815250508b610220015188600a81518110614a6b57614a6b615521565b602002602001018190525061020089015192508083840991508082830991508082840992508287600b81518110614aa457614aa4615521565b6020026020010181815250508b610240015188600b81518110614ac957614ac9615521565b602002602001018190525088610220015181614ae5919061550e565b87600c81518110614af857614af8615521565b6020026020010181815250508b6101a0015188600c81518110614b1d57614b1d615521565b6020026020010181905250600187600d81518110614b3d57614b3d615521565b6020026020010181815250508b6101c0015188600d81518110614b6257614b62615521565b6020026020010181905250806101c08a01516101a08b0151099250806101e08a015184099250806102008a015184099250806102208a0151840992508287600e81518110614bb257614bb2615521565b6020026020010181815250508b610260015188600e81518110614bd757614bd7615521565b60209081029190910101528951614bee908261550e565b87600f81518110614c0157614c01615521565b6020026020010181815250508860c0015188600f81518110614c2557614c25615521565b60200260200101819052508060018b510860a08c0151909350819080099150808284099250808360206010028901510991508187601081518110614c6b57614c6b615521565b6020026020010181815250508860e0015188601081518110614c8f57614c8f615521565b6020026020010181905250808360206011028901510991508187601181518110614cbb57614cbb615521565b60200260200101818152505088610100015188601181518110614ce057614ce0615521565b6020026020010181905250808360206012028901510991508187601281518110614d0c57614d0c615521565b60200260200101818152505088610120015188601281518110614d3157614d31615521565b6020026020010181905250808360206013028901510991508187601381518110614d5d57614d5d615521565b60200260200101818152505088610140015188601381518110614d8257614d82615521565b6020026020010181905250505050505050505050505050565b60405180610100016040528060006001600160401b0316815260200160006001600160401b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518061028001604052806000815260200160008152602001614e14614f30565b8152602001614e21614f30565b8152602001614e2e614f30565b8152602001614e3b614f30565b8152602001614e48614f30565b8152602001614e55614f30565b8152602001614e62614f30565b8152602001614e6f614f30565b8152602001614e7c614f30565b8152602001614e89614f30565b8152602001614e96614f30565b8152602001614ea3614f30565b8152602001614eb0614f30565b8152602001614ebd614f30565b8152602001614eca614f30565b8152602001614ed7614f30565b8152602001614ee4614f30565b8152602001614ef1614f30565b905290565b604051806101000160405280600081526020016000815260200160008152602001600081526020016060815260200160608152602001614ee45b604051806040016040528060008152602001600081525090565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b80356001600160a01b038116811461309457600080fd5b600060208284031215614faf57600080fd5b6120ab82614f86565b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b6040516102e081016001600160401b038111828210171561500557615005614fcc565b60405290565b604051601f8201601f191681016001600160401b038111828210171561503357615033614fcc565b604052919050565b80356001600160401b038116811461309457600080fd5b600061010080838503121561506657600080fd5b604051908101906001600160401b038211818310171561508857615088614fcc565b816040528092506150988461503b565b81526150a66020850161503b565b602082015260408401356040820152606084013560608201526080840135608082015260a084013560a082015260c084013560c082015260e084013560e0820152505092915050565b60006040828403121561510157600080fd5b604080519081016001600160401b038111828210171561512357615123614fcc565b604052823581526020928301359281019290925250919050565b60008082840361058081121561515257600080fd5b61515c8585615052565b92506101006104808060ff198401121561517557600080fd5b61517d614fe2565b925061518b878388016150ef565b835261014061519c888289016150ef565b60208501526101806151b089828a016150ef565b60408601526101c06151c48a828b016150ef565b60608701526102006151d88b828c016150ef565b60808801526102406151ec8c828d016150ef565b60a08901526102806152008d828e016150ef565b60c08a01526102c06152148e828f016150ef565b60e08b01526152278e6103008f016150ef565b898b01526152398e6103408f016150ef565b6101208b015261524d8e6103808f016150ef565b878b015261525f8e6103c08f016150ef565b6101608b01526152738e6104008f016150ef565b868b01526104408d01356101a08b01526104608d0135858b0152878d01356101e08b01526104a08d0135848b01526104c08d01356102208b01526104e08d0135838b01526105008d01356102608b01526105208d0135828b01526105408d01356102a08b01526105608d0135818b0152505050505050505050809150509250929050565b6000806040838503121561530a57600080fd5b61531383614f86565b91506020838101356001600160401b038082111561533057600080fd5b818601915086601f83011261534457600080fd5b81358181111561535657615356614fcc565b615368601f8201601f1916850161500b565b9150808252878482850101111561537e57600080fd5b80848401858401376000848284010152508093505050509250929050565b6001600160401b0391909116815260200190565b803563ffffffff8116811461309457600080fd5b6000602082840312156153d657600080fd5b6120ab826153b0565b6000602082840312156153f157600080fd5b5035919050565b6000806000610140848603121561540e57600080fd5b6154188585615052565b925061542761010085016153b0565b91506154366101208501614f86565b90509250925092565b6000610100828403121561545257600080fd5b6120ab8383615052565b60005b8381101561547757818101518382015260200161545f565b50506000910152565b602081526000825180602084015261549f81604085016020870161545c565b601f01601f19169190910160400192915050565b600080604083850312156154c657600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6001600160401b038181168382160280821691908281146141e9576141e96154d5565b8181038181111561101a5761101a6154d5565b634e487b7160e01b600052603260045260246000fd5b600081615546576155466154d5565b506000190190565b6001600160401b03818116838216019080821115613215576132156154d5565b60006020828403121561558057600080fd5b5051919050565b8082018082111561101a5761101a6154d5565b600082516155ac81846020870161545c565b9190910192915050565b6000875160206155c98285838d0161545c565b8851918401916155dc8184848d0161545c565b88519201916155ee8184848c0161545c565b87519201916156008184848b0161545c565b86519201916156128184848a0161545c565b8551920191615624818484890161545c565b919091019998505050505050505050565b6000835161564781846020880161545c565b83519083019061565b81836020880161545c565b01949350505050565b60008751615676818460208c0161545c565b9190910195865250602085019390935260408401919091526060830152608082015260a001919050565b600084516156b281846020890161545c565b8451908301906156c681836020890161545c565b84519101906156d981836020880161545c565b0195945050505050565b634e487b7160e01b600052601260045260246000fd5b60008261571657634e487b7160e01b600052601260045260246000fd5b500690565b6000855161572d818460208a0161545c565b855190830190615741818360208a0161545c565b855191019061575481836020890161545c565b845191019061576781836020880161545c565b019695505050505050565b6000895160206157858285838f0161545c565b8a51918401916157988184848f0161545c565b8a519201916157aa8184848e0161545c565b89519201916157bc8184848d0161545c565b88519201916157ce8184848c0161545c565b87519201916157e08184848b0161545c565b86519201916157f28184848a0161545c565b8551920191615804818484890161545c565b919091019b9a5050505050505050505050565b60008851602061582a8285838e0161545c565b89519184019161583d8184848e0161545c565b895192019161584f8184848d0161545c565b88519201916158618184848c0161545c565b87519201916158738184848b0161545c565b86519201916158858184848a0161545c565b8551920191615897818484890161545c565b919091019a9950505050505050505050565b60008a516158bb818460208f0161545c565b9190910198895250602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000191905056fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6882e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e881360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001f3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee31ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb025f3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee42042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4aa164736f6c6343000817000a" diff --git a/contract-bindings/src/erc1967_proxy.rs b/contract-bindings/src/erc1967_proxy.rs index 08d99abf0..b3e9992b6 100644 --- a/contract-bindings/src/erc1967_proxy.rs +++ b/contract-bindings/src/erc1967_proxy.rs @@ -94,12 +94,12 @@ pub mod erc1967_proxy { pub static ERC1967PROXY_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R`@Qa\x03\xEE8\x03\x80a\x03\xEE\x839\x81\x01`@\x81\x90Ra\0\"\x91a\x02hV[a\0,\x82\x82a\x003V[PPa\x03RV[a\0<\x82a\0\x92V[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a\0\x86Wa\0\x81\x82\x82a\x01\x0EV[PPPV[a\0\x8Ea\x01\x85V[PPV[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a\0\xCDW`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16`\x04\x82\x01R`$\x01[`@Q\x80\x91\x03\x90\xFD[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa\x01+\x91\x90a\x036V[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a\x01fW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\x01kV[``\x91P[P\x90\x92P\x90Pa\x01|\x85\x83\x83a\x01\xA6V[\x95\x94PPPPPV[4\x15a\x01\xA4W`@Qc\xB3\x98\x97\x9F`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[V[``\x82a\x01\xBBWa\x01\xB6\x82a\x02\x05V[a\x01\xFEV[\x81Q\x15\x80\x15a\x01\xD2WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a\x01\xFBW`@Qc\x99\x96\xB3\x15`\xE0\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x85\x16`\x04\x82\x01R`$\x01a\0\xC4V[P\x80[\x93\x92PPPV[\x80Q\x15a\x02\x15W\x80Q\x80\x82` \x01\xFD[`@Qc\n\x12\xF5!`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0[\x83\x81\x10\x15a\x02_W\x81\x81\x01Q\x83\x82\x01R` \x01a\x02GV[PP`\0\x91\x01RV[`\0\x80`@\x83\x85\x03\x12\x15a\x02{W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x02\x92W`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x01`\x01`@\x1B\x03\x80\x82\x11\x15a\x02\xAFW`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\x02\xC3W`\0\x80\xFD[\x81Q\x81\x81\x11\x15a\x02\xD5Wa\x02\xD5a\x02.V[`@Q`\x1F\x82\x01`\x1F\x19\x90\x81\x16`?\x01\x16\x81\x01\x90\x83\x82\x11\x81\x83\x10\x17\x15a\x02\xFDWa\x02\xFDa\x02.V[\x81`@R\x82\x81R\x88` \x84\x87\x01\x01\x11\x15a\x03\x16W`\0\x80\xFD[a\x03'\x83` \x83\x01` \x88\x01a\x02DV[\x80\x95PPPPPP\x92P\x92\x90PV[`\0\x82Qa\x03H\x81\x84` \x87\x01a\x02DV[\x91\x90\x91\x01\x92\x91PPV[`\x8E\x80a\x03``\09`\0\xF3\xFE`\x80`@R`\n`\x0CV[\0[`\x18`\x14`\x1AV[`^V[V[`\0`Y\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBCTs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x90V[\x90P\x90V[6`\0\x807`\0\x806`\0\x84Z\xF4=`\0\x80>\x80\x80\x15`|W=`\0\xF3[=`\0\xFD\xFE\xA1dsolcC\0\x08\x17\0\n"; + const __BYTECODE: &[u8] = b"`\x80`@R`@Qa\x03\xE78\x03\x80a\x03\xE7\x839\x81\x01`@\x81\x90Ra\0\"\x91a\x02ZV[a\0,\x82\x82a\x003V[PPa\x03XV[a\0<\x82a\0\x92V[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a\0\x86Wa\0\x81\x82\x82a\x01\tV[PPPV[a\0\x8Ea\x01\x80V[PPV[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a\0\xC8W\x80`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\0\xBF\x91\x90a\x03(V[`@Q\x80\x91\x03\x90\xFD[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa\x01&\x91\x90a\x03a\x01fV[``\x91P[P\x90\x92P\x90Pa\x01w\x85\x83\x83a\x01\xA1V[\x95\x94PPPPPV[4\x15a\x01\x9FW`@Qc\xB3\x98\x97\x9F`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[V[``\x82a\x01\xB6Wa\x01\xB1\x82a\x01\xF7V[a\x01\xF0V[\x81Q\x15\x80\x15a\x01\xCDWP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a\x01\xEDW\x83`@Qc\x99\x96\xB3\x15`\xE0\x1B\x81R`\x04\x01a\0\xBF\x91\x90a\x03(V[P\x80[\x93\x92PPPV[\x80Q\x15a\x02\x07W\x80Q\x80\x82` \x01\xFD[`@Qc\n\x12\xF5!`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0[\x83\x81\x10\x15a\x02QW\x81\x81\x01Q\x83\x82\x01R` \x01a\x029V[PP`\0\x91\x01RV[`\0\x80`@\x83\x85\x03\x12\x15a\x02mW`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x02\x84W`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x01`\x01`@\x1B\x03\x80\x82\x11\x15a\x02\xA1W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\x02\xB5W`\0\x80\xFD[\x81Q\x81\x81\x11\x15a\x02\xC7Wa\x02\xC7a\x02 V[`@Q`\x1F\x82\x01`\x1F\x19\x90\x81\x16`?\x01\x16\x81\x01\x90\x83\x82\x11\x81\x83\x10\x17\x15a\x02\xEFWa\x02\xEFa\x02 V[\x81`@R\x82\x81R\x88` \x84\x87\x01\x01\x11\x15a\x03\x08W`\0\x80\xFD[a\x03\x19\x83` \x83\x01` \x88\x01a\x026V[\x80\x95PPPPPP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[`\0\x82Qa\x03N\x81\x84` \x87\x01a\x026V[\x91\x90\x91\x01\x92\x91PPV[`\x81\x80a\x03f`\09`\0\xF3\xFE`\x80`@R`\n`\x0CV[\0[`\x18`\x14`\x1AV[`QV[V[`\0`L\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBCT`\x01`\x01`\xA0\x1B\x03\x16\x90V[\x90P\x90V[6`\0\x807`\0\x806`\0\x84Z\xF4=`\0\x80>\x80\x80\x15`oW=`\0\xF3[=`\0\xFD\xFE\xA1dsolcC\0\x08\x17\0\n"; /// The bytecode of the contract. pub static ERC1967PROXY_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(__BYTECODE); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\n`\x0CV[\0[`\x18`\x14`\x1AV[`^V[V[`\0`Y\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBCTs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x90V[\x90P\x90V[6`\0\x807`\0\x806`\0\x84Z\xF4=`\0\x80>\x80\x80\x15`|W=`\0\xF3[=`\0\xFD\xFE\xA1dsolcC\0\x08\x17\0\n"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\n`\x0CV[\0[`\x18`\x14`\x1AV[`QV[V[`\0`L\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBCT`\x01`\x01`\xA0\x1B\x03\x16\x90V[\x90P\x90V[6`\0\x807`\0\x806`\0\x84Z\xF4=`\0\x80>\x80\x80\x15`oW=`\0\xF3[=`\0\xFD\xFE\xA1dsolcC\0\x08\x17\0\n"; /// The deployed bytecode of the contract. pub static ERC1967PROXY_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(__DEPLOYED_BYTECODE); diff --git a/contract-bindings/src/fee_contract.rs b/contract-bindings/src/fee_contract.rs index 48968ef88..bcb9e9476 100644 --- a/contract-bindings/src/fee_contract.rs +++ b/contract-bindings/src/fee_contract.rs @@ -475,12 +475,12 @@ pub mod fee_contract { pub static FEECONTRACT_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\xA0`@R0`\x80R4\x80\x15a\0\x14W`\0\x80\xFD[Pa\0\x1Da\0\"V[a\0\xD4V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15a\0rW`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80T`\x01`\x01`@\x1B\x03\x90\x81\x16\x14a\0\xD1W\x80T`\x01`\x01`@\x1B\x03\x19\x16`\x01`\x01`@\x1B\x03\x90\x81\x17\x82U`@Q\x90\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PV[`\x80Qa\x0E\xBAa\0\xFD`\09`\0\x81\x81a\x06^\x01R\x81\x81a\x06\x87\x01Ra\x08{\x01Ra\x0E\xBA`\0\xF3\xFE`\x80`@R`\x046\x10a\0\xCBW`\x005`\xE0\x1C\x80c\x8D\xA5\xCB[\x11a\0tW\x80c\xC4\xD6m\xE8\x11a\0NW\x80c\xC4\xD6m\xE8\x14a\x02\xA9W\x80c\xF2\xFD\xE3\x8B\x14a\x02\xC9W\x80c\xF3@\xFA\x01\x14a\x02\xE9Wa\x01\x02V[\x80c\x8D\xA5\xCB[\x14a\x01\xF6W\x80c\x8E\xD82q\x14a\x02=W\x80c\xAD<\xB1\xCC\x14a\x02SWa\x01\x02V[\x80cR\xD1\x90-\x11a\0\xA5W\x80cR\xD1\x90-\x14a\x01\xB6W\x80cdP\x06\xCA\x14a\x01\xCBW\x80cqP\x18\xA6\x14a\x01\xE1Wa\x01\x02V[\x80c\r\x8En,\x14a\x014W\x80c'\xE25\xE3\x14a\x01fW\x80cO\x1E\xF2\x86\x14a\x01\xA1Wa\x01\x02V[6a\x01\x02W`@Q\x7F\xBC\x8E\xCA\x1B\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`@Q\x7F\xA9\xADb\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[4\x80\x15a\x01@W`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01rW`\0\x80\xFD[Pa\x01\x93a\x01\x816`\x04a\x0C\xDBV[`\x02` R`\0\x90\x81R`@\x90 T\x81V[`@Q\x90\x81R` \x01a\x01]V[a\x01\xB4a\x01\xAF6`\x04a\r%V[a\x02\xFCV[\0[4\x80\x15a\x01\xC2W`\0\x80\xFD[Pa\x01\x93a\x03\x1BV[4\x80\x15a\x01\xD7W`\0\x80\xFD[Pa\x01\x93`\x01T\x81V[4\x80\x15a\x01\xEDW`\0\x80\xFD[Pa\x01\xB4a\x03JV[4\x80\x15a\x02\x02W`\0\x80\xFD[P\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01]V[4\x80\x15a\x02IW`\0\x80\xFD[Pa\x01\x93`\0T\x81V[4\x80\x15a\x02_W`\0\x80\xFD[Pa\x02\x9C`@Q\x80`@\x01`@R\x80`\x05\x81R` \x01\x7F5.0.0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81RP\x81V[`@Qa\x01]\x91\x90a\x0E\x0BV[4\x80\x15a\x02\xB5W`\0\x80\xFD[Pa\x01\xB4a\x02\xC46`\x04a\x0C\xDBV[a\x03^V[4\x80\x15a\x02\xD5W`\0\x80\xFD[Pa\x01\xB4a\x02\xE46`\x04a\x0C\xDBV[a\x04\xD1V[a\x01\xB4a\x02\xF76`\x04a\x0C\xDBV[a\x05-V[a\x03\x04a\x06SV[a\x03\r\x82a\x07#V[a\x03\x17\x82\x82a\x07jV[PPV[`\0a\x03%a\x08pV[P\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x90V[a\x03Ra\x08\xD2V[a\x03\\`\0a\tFV[V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x81\x04`\xFF\x16\x15\x90g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\0\x81\x15\x80\x15a\x03\xA9WP\x82[\x90P`\0\x82g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\x01\x14\x80\x15a\x03\xC6WP0;\x15[\x90P\x81\x15\x80\x15a\x03\xD4WP\x80\x15[\x15a\x04\x0BW`@Q\x7F\xF9.\xE8\xA9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\x16`\x01\x17\x85U\x83\x15a\x04VW\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16h\x01\0\0\0\0\0\0\0\0\x17\x85U[a\x04_\x86a\t\xCFV[a\x04ga\t\xE0V[g\r\xE0\xB6\xB3\xA7d\0\0`\0Uf\x03\x8D~\xA4\xC6\x80\0`\x01U\x83\x15a\x04\xC9W\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16\x85U`@Q`\x01\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPV[a\x04\xD9a\x08\xD2V[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x05!W`@Q\x7F\x1EO\xBD\xF7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\0`\x04\x82\x01R`$\x01[`@Q\x80\x91\x03\x90\xFD[a\x05*\x81a\tFV[PV[`\x01T4\x10\x15a\x05iW`@Q\x7Fk\xA4\xA1\xC7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0T4\x11\x15a\x05\xA5W`@Q\x7F\xC5mF\xD3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x05\xE5W`@Q\x7Fp+=\x90\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x16`\0\x90\x81R`\x02` R`@\x81 \x80T4\x92\x90a\x06\r\x90\x84\x90a\x0E>V[\x90\x91UPP`@Q4\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16\x90\x7F\xE1\xFF\xFC\xC4\x92=\x04\xB5Y\xF4\xD2\x9A\x8B\xFCl\xDA\x04\xEB[\r v\xCC75\xA9 \xA3\xCAP]8+\xBCT`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14\x15[\x15a\x03\\W`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x07+a\x08\xD2V[`@Q`\x01`\x01`\xA0\x1B\x03\x82\x16\x81R\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x90` \x01`@Q\x80\x91\x03\x90\xA1PV[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x07\xC4WP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x07\xC1\x91\x81\x01\x90a\x0ExV[`\x01[a\x08\x05W`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16`\x04\x82\x01R`$\x01a\x05\x18V[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x81\x14a\x08aW`@Q\x7F\xAA\x1DI\xA4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x81\x01\x82\x90R`$\x01a\x05\x18V[a\x08k\x83\x83a\t\xE8V[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\x03\\W`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[3a\t\x04\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\x03\\W`@Q\x7F\x11\x8C\xDA\xA7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R3`\x04\x82\x01R`$\x01a\x05\x18V[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x16`\x01`\x01`\xA0\x1B\x03\x84\x81\x16\x91\x82\x17\x84U`@Q\x92\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[a\t\xD7a\n>V[a\x05*\x81a\n\xA5V[a\x03\\a\n>V[a\t\xF1\x82a\n\xADV[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a\n6Wa\x08k\x82\x82a\x0BUV[a\x03\x17a\x0B\xCDV[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16a\x03\\W`@Q\x7F\xD7\xE6\xBC\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x04\xD9a\n>V[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a\n\xFCW`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16`\x04\x82\x01R`$\x01a\x05\x18V[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa\x0Br\x91\x90a\x0E\x91V[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a\x0B\xADW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\x0B\xB2V[``\x91P[P\x91P\x91Pa\x0B\xC2\x85\x83\x83a\x0C\x05V[\x92PPP[\x92\x91PPV[4\x15a\x03\\W`@Q\x7F\xB3\x98\x97\x9F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x82a\x0C\x1AWa\x0C\x15\x82a\x0C}V[a\x0CvV[\x81Q\x15\x80\x15a\x0C1WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a\x0CsW`@Q\x7F\x99\x96\xB3\x15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x85\x16`\x04\x82\x01R`$\x01a\x05\x18V[P\x80[\x93\x92PPPV[\x80Q\x15a\x0C\x8DW\x80Q\x80\x82` \x01\xFD[`@Q\x7F\x14%\xEAB\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0C\xD6W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0C\xEDW`\0\x80\xFD[a\x0Cv\x82a\x0C\xBFV[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\0R`A`\x04R`$`\0\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\r8W`\0\x80\xFD[a\rA\x83a\x0C\xBFV[\x91P` \x83\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x11\x15a\r^W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\rrW`\0\x80\xFD[\x815\x81\x81\x11\x15a\r\x84Wa\r\x84a\x0C\xF6V[`@Q`\x1F\x82\x01`\x1F\x19\x90\x81\x16`?\x01\x16\x81\x01\x90\x83\x82\x11\x81\x83\x10\x17\x15a\r\xACWa\r\xACa\x0C\xF6V[\x81`@R\x82\x81R\x88` \x84\x87\x01\x01\x11\x15a\r\xC5W`\0\x80\xFD[\x82` \x86\x01` \x83\x017`\0` \x84\x83\x01\x01R\x80\x95PPPPPP\x92P\x92\x90PV[`\0[\x83\x81\x10\x15a\x0E\x02W\x81\x81\x01Q\x83\x82\x01R` \x01a\r\xEAV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x0E*\x81`@\x85\x01` \x87\x01a\r\xE7V[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[\x80\x82\x01\x80\x82\x11\x15a\x0B\xC7W\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\0R`\x11`\x04R`$`\0\xFD[`\0` \x82\x84\x03\x12\x15a\x0E\x8AW`\0\x80\xFD[PQ\x91\x90PV[`\0\x82Qa\x0E\xA3\x81\x84` \x87\x01a\r\xE7V[\x91\x90\x91\x01\x92\x91PPV\xFE\xA1dsolcC\0\x08\x17\0\n"; + const __BYTECODE: &[u8] = b"`\xA0`@R0`\x80R4\x80\x15a\0\x14W`\0\x80\xFD[Pa\0\x1Da\0\"V[a\0\xD4V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15a\0rW`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80T`\x01`\x01`@\x1B\x03\x90\x81\x16\x14a\0\xD1W\x80T`\x01`\x01`@\x1B\x03\x19\x16`\x01`\x01`@\x1B\x03\x90\x81\x17\x82U`@Q\x90\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PV[`\x80Qa\x0B\xE5a\0\xFD`\09`\0\x81\x81a\x05\r\x01R\x81\x81a\x056\x01Ra\x06\xAD\x01Ra\x0B\xE5`\0\xF3\xFE`\x80`@R`\x046\x10a\0\x9BW`\x005`\xE0\x1C\x80c\r\x8En,\x14a\0\xD2W\x80c'\xE25\xE3\x14a\x01\x04W\x80cO\x1E\xF2\x86\x14a\x01?W\x80cR\xD1\x90-\x14a\x01TW\x80cdP\x06\xCA\x14a\x01iW\x80cqP\x18\xA6\x14a\x01\x7FW\x80c\x8D\xA5\xCB[\x14a\x01\x94W\x80c\x8E\xD82q\x14a\x01\xB6W\x80c\xAD<\xB1\xCC\x14a\x01\xCCW\x80c\xC4\xD6m\xE8\x14a\x02\nW\x80c\xF2\xFD\xE3\x8B\x14a\x02*W\x80c\xF3@\xFA\x01\x14a\x02JWa\0\xB9V[6a\0\xB9W`@Qc\xBC\x8E\xCA\x1B`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`@Qc\x155\xAC_`\xE3\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[4\x80\x15a\0\xDEW`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x10W`\0\x80\xFD[Pa\x011a\x01\x1F6`\x04a\n\x05V[`\x02` R`\0\x90\x81R`@\x90 T\x81V[`@Q\x90\x81R` \x01a\0\xFBV[a\x01Ra\x01M6`\x04a\n6V[a\x02]V[\0[4\x80\x15a\x01`W`\0\x80\xFD[Pa\x011a\x02|V[4\x80\x15a\x01uW`\0\x80\xFD[Pa\x011`\x01T\x81V[4\x80\x15a\x01\x8BW`\0\x80\xFD[Pa\x01Ra\x02\x99V[4\x80\x15a\x01\xA0W`\0\x80\xFD[Pa\x01\xA9a\x02\xADV[`@Qa\0\xFB\x91\x90a\n\xF7V[4\x80\x15a\x01\xC2W`\0\x80\xFD[Pa\x011`\0T\x81V[4\x80\x15a\x01\xD8W`\0\x80\xFD[Pa\x01\xFD`@Q\x80`@\x01`@R\x80`\x05\x81R` \x01d\x03R\xE3\x02\xE3`\xDC\x1B\x81RP\x81V[`@Qa\0\xFB\x91\x90a\x0B/V[4\x80\x15a\x02\x16W`\0\x80\xFD[Pa\x01Ra\x02%6`\x04a\n\x05V[a\x02\xC8V[4\x80\x15a\x026W`\0\x80\xFD[Pa\x01Ra\x02E6`\x04a\n\x05V[a\x03\xE0V[a\x01Ra\x02X6`\x04a\n\x05V[a\x04'V[a\x02ea\x05\x02V[a\x02n\x82a\x05\xA7V[a\x02x\x82\x82a\x05\xE9V[PPV[`\0a\x02\x86a\x06\xA2V[P`\0\x80Q` a\x0B\xB9\x839\x81Q\x91R\x90V[a\x02\xA1a\x06\xEBV[a\x02\xAB`\0a\x07\x1DV[V[`\0\x80a\x02\xB8a\x07yV[T`\x01`\x01`\xA0\x1B\x03\x16\x92\x91PPV[`\0a\x02\xD2a\x07\x9DV[\x80T\x90\x91P`\xFF`\x01`@\x1B\x82\x04\x16\x15\x90`\x01`\x01`@\x1B\x03\x16`\0\x81\x15\x80\x15a\x02\xF9WP\x82[\x90P`\0\x82`\x01`\x01`@\x1B\x03\x16`\x01\x14\x80\x15a\x03\x15WP0;\x15[\x90P\x81\x15\x80\x15a\x03#WP\x80\x15[\x15a\x03AW`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T`\x01`\x01`@\x1B\x03\x19\x16`\x01\x17\x85U\x83\x15a\x03jW\x84T`\xFF`@\x1B\x19\x16`\x01`@\x1B\x17\x85U[a\x03s\x86a\x07\xC1V[a\x03{a\x07\xD2V[g\r\xE0\xB6\xB3\xA7d\0\0`\0Uf\x03\x8D~\xA4\xC6\x80\0`\x01U\x83\x15a\x03\xD8W\x84T`\xFF`@\x1B\x19\x16\x85U`@Q`\x01\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPV[a\x03\xE8a\x06\xEBV[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x04\x1BW`\0`@Qc\x1EO\xBD\xF7`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[`@Q\x80\x91\x03\x90\xFD[a\x04$\x81a\x07\x1DV[PV[`\x01T4\x10\x15a\x04JW`@Qck\xA4\xA1\xC7`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0T4\x11\x15a\x04mW`@Qc\xC5mF\xD3`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x04\x94W`@Qc\x07\x02\xB3\xD9`\xE4\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x16`\0\x90\x81R`\x02` R`@\x81 \x80T4\x92\x90a\x04\xBC\x90\x84\x90a\x0BbV[\x90\x91UPP`@Q4\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16\x90\x7F\xE1\xFF\xFC\xC4\x92=\x04\xB5Y\xF4\xD2\x9A\x8B\xFCl\xDA\x04\xEB[\rF\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x05\xAFa\x06\xEBV[\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x81`@Qa\x05\xDE\x91\x90a\n\xF7V[`@Q\x80\x91\x03\x90\xA1PV[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x06CWP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x06@\x91\x81\x01\x90a\x0B\x83V[`\x01[a\x06bW\x81`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[`\0\x80Q` a\x0B\xB9\x839\x81Q\x91R\x81\x14a\x06\x93W`@Qc*\x87Ri`\xE2\x1B\x81R`\x04\x81\x01\x82\x90R`$\x01a\x04\x12V[a\x06\x9D\x83\x83a\x07\xDAV[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\x02\xABW`@Qcp>F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[3a\x06\xF4a\x02\xADV[`\x01`\x01`\xA0\x1B\x03\x16\x14a\x02\xABW3`@Qc\x11\x8C\xDA\xA7`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[`\0a\x07'a\x07yV[\x80T`\x01`\x01`\xA0\x1B\x03\x84\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x93\x94P\x91\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x90V[a\x07\xC9a\x080V[a\x04$\x81a\x08UV[a\x02\xABa\x080V[a\x07\xE3\x82a\x08]V[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a\x08(Wa\x06\x9D\x82\x82a\x08\xB9V[a\x02xa\t1V[a\x088a\tPV[a\x02\xABW`@Qc\x1A\xFC\xD7\x9F`\xE3\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x03\xE8a\x080V[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a\x08\x8AW\x80`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[`\0\x80Q` a\x0B\xB9\x839\x81Q\x91R\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa\x08\xD6\x91\x90a\x0B\x9CV[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a\t\x11W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\t\x16V[``\x91P[P\x91P\x91Pa\t&\x85\x83\x83a\tjV[\x92PPP[\x92\x91PPV[4\x15a\x02\xABW`@Qc\xB3\x98\x97\x9F`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\tZa\x07\x9DV[T`\x01`@\x1B\x90\x04`\xFF\x16\x91\x90PV[``\x82a\t\x7FWa\tz\x82a\t\xC0V[a\t\xB9V[\x81Q\x15\x80\x15a\t\x96WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a\t\xB6W\x83`@Qc\x99\x96\xB3\x15`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[P\x80[\x93\x92PPPV[\x80Q\x15a\t\xD0W\x80Q\x80\x82` \x01\xFD[`@Qc\n\x12\xF5!`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\n\0W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\n\x17W`\0\x80\xFD[a\t\xB9\x82a\t\xE9V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\nIW`\0\x80\xFD[a\nR\x83a\t\xE9V[\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x80\x82\x11\x15a\nnW`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\n\x82W`\0\x80\xFD[\x815\x81\x81\x11\x15a\n\x94Wa\n\x94a\n V[`@Q`\x1F\x82\x01`\x1F\x19\x90\x81\x16`?\x01\x16\x81\x01\x90\x83\x82\x11\x81\x83\x10\x17\x15a\n\xBCWa\n\xBCa\n V[\x81`@R\x82\x81R\x88` \x84\x87\x01\x01\x11\x15a\n\xD5W`\0\x80\xFD[\x82` \x86\x01` \x83\x017`\0` \x84\x83\x01\x01R\x80\x95PPPPPP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[`\0[\x83\x81\x10\x15a\x0B&W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0B\x0EV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x0BN\x81`@\x85\x01` \x87\x01a\x0B\x0BV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[\x80\x82\x01\x80\x82\x11\x15a\t+WcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0` \x82\x84\x03\x12\x15a\x0B\x95W`\0\x80\xFD[PQ\x91\x90PV[`\0\x82Qa\x0B\xAE\x81\x84` \x87\x01a\x0B\x0BV[\x91\x90\x91\x01\x92\x91PPV\xFE6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\xA1dsolcC\0\x08\x17\0\n"; /// The bytecode of the contract. pub static FEECONTRACT_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(__BYTECODE); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\x046\x10a\0\xCBW`\x005`\xE0\x1C\x80c\x8D\xA5\xCB[\x11a\0tW\x80c\xC4\xD6m\xE8\x11a\0NW\x80c\xC4\xD6m\xE8\x14a\x02\xA9W\x80c\xF2\xFD\xE3\x8B\x14a\x02\xC9W\x80c\xF3@\xFA\x01\x14a\x02\xE9Wa\x01\x02V[\x80c\x8D\xA5\xCB[\x14a\x01\xF6W\x80c\x8E\xD82q\x14a\x02=W\x80c\xAD<\xB1\xCC\x14a\x02SWa\x01\x02V[\x80cR\xD1\x90-\x11a\0\xA5W\x80cR\xD1\x90-\x14a\x01\xB6W\x80cdP\x06\xCA\x14a\x01\xCBW\x80cqP\x18\xA6\x14a\x01\xE1Wa\x01\x02V[\x80c\r\x8En,\x14a\x014W\x80c'\xE25\xE3\x14a\x01fW\x80cO\x1E\xF2\x86\x14a\x01\xA1Wa\x01\x02V[6a\x01\x02W`@Q\x7F\xBC\x8E\xCA\x1B\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`@Q\x7F\xA9\xADb\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[4\x80\x15a\x01@W`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01rW`\0\x80\xFD[Pa\x01\x93a\x01\x816`\x04a\x0C\xDBV[`\x02` R`\0\x90\x81R`@\x90 T\x81V[`@Q\x90\x81R` \x01a\x01]V[a\x01\xB4a\x01\xAF6`\x04a\r%V[a\x02\xFCV[\0[4\x80\x15a\x01\xC2W`\0\x80\xFD[Pa\x01\x93a\x03\x1BV[4\x80\x15a\x01\xD7W`\0\x80\xFD[Pa\x01\x93`\x01T\x81V[4\x80\x15a\x01\xEDW`\0\x80\xFD[Pa\x01\xB4a\x03JV[4\x80\x15a\x02\x02W`\0\x80\xFD[P\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01]V[4\x80\x15a\x02IW`\0\x80\xFD[Pa\x01\x93`\0T\x81V[4\x80\x15a\x02_W`\0\x80\xFD[Pa\x02\x9C`@Q\x80`@\x01`@R\x80`\x05\x81R` \x01\x7F5.0.0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81RP\x81V[`@Qa\x01]\x91\x90a\x0E\x0BV[4\x80\x15a\x02\xB5W`\0\x80\xFD[Pa\x01\xB4a\x02\xC46`\x04a\x0C\xDBV[a\x03^V[4\x80\x15a\x02\xD5W`\0\x80\xFD[Pa\x01\xB4a\x02\xE46`\x04a\x0C\xDBV[a\x04\xD1V[a\x01\xB4a\x02\xF76`\x04a\x0C\xDBV[a\x05-V[a\x03\x04a\x06SV[a\x03\r\x82a\x07#V[a\x03\x17\x82\x82a\x07jV[PPV[`\0a\x03%a\x08pV[P\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x90V[a\x03Ra\x08\xD2V[a\x03\\`\0a\tFV[V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x81\x04`\xFF\x16\x15\x90g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\0\x81\x15\x80\x15a\x03\xA9WP\x82[\x90P`\0\x82g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\x01\x14\x80\x15a\x03\xC6WP0;\x15[\x90P\x81\x15\x80\x15a\x03\xD4WP\x80\x15[\x15a\x04\x0BW`@Q\x7F\xF9.\xE8\xA9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\x16`\x01\x17\x85U\x83\x15a\x04VW\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16h\x01\0\0\0\0\0\0\0\0\x17\x85U[a\x04_\x86a\t\xCFV[a\x04ga\t\xE0V[g\r\xE0\xB6\xB3\xA7d\0\0`\0Uf\x03\x8D~\xA4\xC6\x80\0`\x01U\x83\x15a\x04\xC9W\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16\x85U`@Q`\x01\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPV[a\x04\xD9a\x08\xD2V[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x05!W`@Q\x7F\x1EO\xBD\xF7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\0`\x04\x82\x01R`$\x01[`@Q\x80\x91\x03\x90\xFD[a\x05*\x81a\tFV[PV[`\x01T4\x10\x15a\x05iW`@Q\x7Fk\xA4\xA1\xC7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0T4\x11\x15a\x05\xA5W`@Q\x7F\xC5mF\xD3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x05\xE5W`@Q\x7Fp+=\x90\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x16`\0\x90\x81R`\x02` R`@\x81 \x80T4\x92\x90a\x06\r\x90\x84\x90a\x0E>V[\x90\x91UPP`@Q4\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16\x90\x7F\xE1\xFF\xFC\xC4\x92=\x04\xB5Y\xF4\xD2\x9A\x8B\xFCl\xDA\x04\xEB[\r v\xCC75\xA9 \xA3\xCAP]8+\xBCT`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14\x15[\x15a\x03\\W`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x07+a\x08\xD2V[`@Q`\x01`\x01`\xA0\x1B\x03\x82\x16\x81R\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x90` \x01`@Q\x80\x91\x03\x90\xA1PV[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x07\xC4WP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x07\xC1\x91\x81\x01\x90a\x0ExV[`\x01[a\x08\x05W`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16`\x04\x82\x01R`$\x01a\x05\x18V[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x81\x14a\x08aW`@Q\x7F\xAA\x1DI\xA4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x81\x01\x82\x90R`$\x01a\x05\x18V[a\x08k\x83\x83a\t\xE8V[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\x03\\W`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[3a\t\x04\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\x03\\W`@Q\x7F\x11\x8C\xDA\xA7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R3`\x04\x82\x01R`$\x01a\x05\x18V[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x16`\x01`\x01`\xA0\x1B\x03\x84\x81\x16\x91\x82\x17\x84U`@Q\x92\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[a\t\xD7a\n>V[a\x05*\x81a\n\xA5V[a\x03\\a\n>V[a\t\xF1\x82a\n\xADV[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a\n6Wa\x08k\x82\x82a\x0BUV[a\x03\x17a\x0B\xCDV[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16a\x03\\W`@Q\x7F\xD7\xE6\xBC\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x04\xD9a\n>V[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a\n\xFCW`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16`\x04\x82\x01R`$\x01a\x05\x18V[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa\x0Br\x91\x90a\x0E\x91V[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a\x0B\xADW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\x0B\xB2V[``\x91P[P\x91P\x91Pa\x0B\xC2\x85\x83\x83a\x0C\x05V[\x92PPP[\x92\x91PPV[4\x15a\x03\\W`@Q\x7F\xB3\x98\x97\x9F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x82a\x0C\x1AWa\x0C\x15\x82a\x0C}V[a\x0CvV[\x81Q\x15\x80\x15a\x0C1WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a\x0CsW`@Q\x7F\x99\x96\xB3\x15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x85\x16`\x04\x82\x01R`$\x01a\x05\x18V[P\x80[\x93\x92PPPV[\x80Q\x15a\x0C\x8DW\x80Q\x80\x82` \x01\xFD[`@Q\x7F\x14%\xEAB\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0C\xD6W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0C\xEDW`\0\x80\xFD[a\x0Cv\x82a\x0C\xBFV[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\0R`A`\x04R`$`\0\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\r8W`\0\x80\xFD[a\rA\x83a\x0C\xBFV[\x91P` \x83\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x11\x15a\r^W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\rrW`\0\x80\xFD[\x815\x81\x81\x11\x15a\r\x84Wa\r\x84a\x0C\xF6V[`@Q`\x1F\x82\x01`\x1F\x19\x90\x81\x16`?\x01\x16\x81\x01\x90\x83\x82\x11\x81\x83\x10\x17\x15a\r\xACWa\r\xACa\x0C\xF6V[\x81`@R\x82\x81R\x88` \x84\x87\x01\x01\x11\x15a\r\xC5W`\0\x80\xFD[\x82` \x86\x01` \x83\x017`\0` \x84\x83\x01\x01R\x80\x95PPPPPP\x92P\x92\x90PV[`\0[\x83\x81\x10\x15a\x0E\x02W\x81\x81\x01Q\x83\x82\x01R` \x01a\r\xEAV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x0E*\x81`@\x85\x01` \x87\x01a\r\xE7V[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[\x80\x82\x01\x80\x82\x11\x15a\x0B\xC7W\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\0R`\x11`\x04R`$`\0\xFD[`\0` \x82\x84\x03\x12\x15a\x0E\x8AW`\0\x80\xFD[PQ\x91\x90PV[`\0\x82Qa\x0E\xA3\x81\x84` \x87\x01a\r\xE7V[\x91\x90\x91\x01\x92\x91PPV\xFE\xA1dsolcC\0\x08\x17\0\n"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\x046\x10a\0\x9BW`\x005`\xE0\x1C\x80c\r\x8En,\x14a\0\xD2W\x80c'\xE25\xE3\x14a\x01\x04W\x80cO\x1E\xF2\x86\x14a\x01?W\x80cR\xD1\x90-\x14a\x01TW\x80cdP\x06\xCA\x14a\x01iW\x80cqP\x18\xA6\x14a\x01\x7FW\x80c\x8D\xA5\xCB[\x14a\x01\x94W\x80c\x8E\xD82q\x14a\x01\xB6W\x80c\xAD<\xB1\xCC\x14a\x01\xCCW\x80c\xC4\xD6m\xE8\x14a\x02\nW\x80c\xF2\xFD\xE3\x8B\x14a\x02*W\x80c\xF3@\xFA\x01\x14a\x02JWa\0\xB9V[6a\0\xB9W`@Qc\xBC\x8E\xCA\x1B`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`@Qc\x155\xAC_`\xE3\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[4\x80\x15a\0\xDEW`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x10W`\0\x80\xFD[Pa\x011a\x01\x1F6`\x04a\n\x05V[`\x02` R`\0\x90\x81R`@\x90 T\x81V[`@Q\x90\x81R` \x01a\0\xFBV[a\x01Ra\x01M6`\x04a\n6V[a\x02]V[\0[4\x80\x15a\x01`W`\0\x80\xFD[Pa\x011a\x02|V[4\x80\x15a\x01uW`\0\x80\xFD[Pa\x011`\x01T\x81V[4\x80\x15a\x01\x8BW`\0\x80\xFD[Pa\x01Ra\x02\x99V[4\x80\x15a\x01\xA0W`\0\x80\xFD[Pa\x01\xA9a\x02\xADV[`@Qa\0\xFB\x91\x90a\n\xF7V[4\x80\x15a\x01\xC2W`\0\x80\xFD[Pa\x011`\0T\x81V[4\x80\x15a\x01\xD8W`\0\x80\xFD[Pa\x01\xFD`@Q\x80`@\x01`@R\x80`\x05\x81R` \x01d\x03R\xE3\x02\xE3`\xDC\x1B\x81RP\x81V[`@Qa\0\xFB\x91\x90a\x0B/V[4\x80\x15a\x02\x16W`\0\x80\xFD[Pa\x01Ra\x02%6`\x04a\n\x05V[a\x02\xC8V[4\x80\x15a\x026W`\0\x80\xFD[Pa\x01Ra\x02E6`\x04a\n\x05V[a\x03\xE0V[a\x01Ra\x02X6`\x04a\n\x05V[a\x04'V[a\x02ea\x05\x02V[a\x02n\x82a\x05\xA7V[a\x02x\x82\x82a\x05\xE9V[PPV[`\0a\x02\x86a\x06\xA2V[P`\0\x80Q` a\x0B\xB9\x839\x81Q\x91R\x90V[a\x02\xA1a\x06\xEBV[a\x02\xAB`\0a\x07\x1DV[V[`\0\x80a\x02\xB8a\x07yV[T`\x01`\x01`\xA0\x1B\x03\x16\x92\x91PPV[`\0a\x02\xD2a\x07\x9DV[\x80T\x90\x91P`\xFF`\x01`@\x1B\x82\x04\x16\x15\x90`\x01`\x01`@\x1B\x03\x16`\0\x81\x15\x80\x15a\x02\xF9WP\x82[\x90P`\0\x82`\x01`\x01`@\x1B\x03\x16`\x01\x14\x80\x15a\x03\x15WP0;\x15[\x90P\x81\x15\x80\x15a\x03#WP\x80\x15[\x15a\x03AW`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T`\x01`\x01`@\x1B\x03\x19\x16`\x01\x17\x85U\x83\x15a\x03jW\x84T`\xFF`@\x1B\x19\x16`\x01`@\x1B\x17\x85U[a\x03s\x86a\x07\xC1V[a\x03{a\x07\xD2V[g\r\xE0\xB6\xB3\xA7d\0\0`\0Uf\x03\x8D~\xA4\xC6\x80\0`\x01U\x83\x15a\x03\xD8W\x84T`\xFF`@\x1B\x19\x16\x85U`@Q`\x01\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPV[a\x03\xE8a\x06\xEBV[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x04\x1BW`\0`@Qc\x1EO\xBD\xF7`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[`@Q\x80\x91\x03\x90\xFD[a\x04$\x81a\x07\x1DV[PV[`\x01T4\x10\x15a\x04JW`@Qck\xA4\xA1\xC7`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0T4\x11\x15a\x04mW`@Qc\xC5mF\xD3`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x04\x94W`@Qc\x07\x02\xB3\xD9`\xE4\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x16`\0\x90\x81R`\x02` R`@\x81 \x80T4\x92\x90a\x04\xBC\x90\x84\x90a\x0BbV[\x90\x91UPP`@Q4\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16\x90\x7F\xE1\xFF\xFC\xC4\x92=\x04\xB5Y\xF4\xD2\x9A\x8B\xFCl\xDA\x04\xEB[\rF\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x05\xAFa\x06\xEBV[\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x81`@Qa\x05\xDE\x91\x90a\n\xF7V[`@Q\x80\x91\x03\x90\xA1PV[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x06CWP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x06@\x91\x81\x01\x90a\x0B\x83V[`\x01[a\x06bW\x81`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[`\0\x80Q` a\x0B\xB9\x839\x81Q\x91R\x81\x14a\x06\x93W`@Qc*\x87Ri`\xE2\x1B\x81R`\x04\x81\x01\x82\x90R`$\x01a\x04\x12V[a\x06\x9D\x83\x83a\x07\xDAV[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\x02\xABW`@Qcp>F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[3a\x06\xF4a\x02\xADV[`\x01`\x01`\xA0\x1B\x03\x16\x14a\x02\xABW3`@Qc\x11\x8C\xDA\xA7`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[`\0a\x07'a\x07yV[\x80T`\x01`\x01`\xA0\x1B\x03\x84\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x93\x94P\x91\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x90V[a\x07\xC9a\x080V[a\x04$\x81a\x08UV[a\x02\xABa\x080V[a\x07\xE3\x82a\x08]V[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a\x08(Wa\x06\x9D\x82\x82a\x08\xB9V[a\x02xa\t1V[a\x088a\tPV[a\x02\xABW`@Qc\x1A\xFC\xD7\x9F`\xE3\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x03\xE8a\x080V[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a\x08\x8AW\x80`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[`\0\x80Q` a\x0B\xB9\x839\x81Q\x91R\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa\x08\xD6\x91\x90a\x0B\x9CV[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a\t\x11W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\t\x16V[``\x91P[P\x91P\x91Pa\t&\x85\x83\x83a\tjV[\x92PPP[\x92\x91PPV[4\x15a\x02\xABW`@Qc\xB3\x98\x97\x9F`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\tZa\x07\x9DV[T`\x01`@\x1B\x90\x04`\xFF\x16\x91\x90PV[``\x82a\t\x7FWa\tz\x82a\t\xC0V[a\t\xB9V[\x81Q\x15\x80\x15a\t\x96WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a\t\xB6W\x83`@Qc\x99\x96\xB3\x15`\xE0\x1B\x81R`\x04\x01a\x04\x12\x91\x90a\n\xF7V[P\x80[\x93\x92PPPV[\x80Q\x15a\t\xD0W\x80Q\x80\x82` \x01\xFD[`@Qc\n\x12\xF5!`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\n\0W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\n\x17W`\0\x80\xFD[a\t\xB9\x82a\t\xE9V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\nIW`\0\x80\xFD[a\nR\x83a\t\xE9V[\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x80\x82\x11\x15a\nnW`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\n\x82W`\0\x80\xFD[\x815\x81\x81\x11\x15a\n\x94Wa\n\x94a\n V[`@Q`\x1F\x82\x01`\x1F\x19\x90\x81\x16`?\x01\x16\x81\x01\x90\x83\x82\x11\x81\x83\x10\x17\x15a\n\xBCWa\n\xBCa\n V[\x81`@R\x82\x81R\x88` \x84\x87\x01\x01\x11\x15a\n\xD5W`\0\x80\xFD[\x82` \x86\x01` \x83\x017`\0` \x84\x83\x01\x01R\x80\x95PPPPPP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[`\0[\x83\x81\x10\x15a\x0B&W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0B\x0EV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x0BN\x81`@\x85\x01` \x87\x01a\x0B\x0BV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[\x80\x82\x01\x80\x82\x11\x15a\t+WcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0` \x82\x84\x03\x12\x15a\x0B\x95W`\0\x80\xFD[PQ\x91\x90PV[`\0\x82Qa\x0B\xAE\x81\x84` \x87\x01a\x0B\x0BV[\x91\x90\x91\x01\x92\x91PPV\xFE6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\xA1dsolcC\0\x08\x17\0\n"; /// The deployed bytecode of the contract. pub static FEECONTRACT_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(__DEPLOYED_BYTECODE); diff --git a/contract-bindings/src/hot_shot.rs b/contract-bindings/src/hot_shot.rs index e1a101b3a..792d4644d 100644 --- a/contract-bindings/src/hot_shot.rs +++ b/contract-bindings/src/hot_shot.rs @@ -283,12 +283,12 @@ pub mod hot_shot { pub static HOTSHOT_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[Pa\x06\x07\x80a\0 `\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0rW`\x005`\xE0\x1C\x80cg\xA2\x1Ep\x11a\0PW\x80cg\xA2\x1Ep\x14a\0\xC8W\x80c\xF1\xF4]\x99\x14a\x01\x12W\x80c\xF4O\xF7\x12\x14a\x01%W`\0\x80\xFD[\x80c\n2\x1C\xFF\x14a\0wW\x80c&\x83=\xCC\x14a\0\x8CW\x80cI\xCE\x89\x97\x14a\0\xA8W[`\0\x80\xFD[a\0\x8Aa\0\x856`\x04a\x04\xA9V[a\x01.V[\0[a\0\x95a\x01\xF4\x81V[`@Q\x90\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\x95a\0\xB66`\x04a\x05\x1EV[`\0` \x81\x90R\x90\x81R`@\x90 T\x81V[a\0\xDBa\0\xD66`\x04a\x05\x1EV[a\x02\xEDV[`@\x80Q\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R\x83\x82\x01Q\x91\x81\x01\x91\x90\x91R``\x92\x83\x01Q\x92\x81\x01\x92\x90\x92R`\x80\x82\x01R`\xA0\x01a\0\x9FV[a\0\x8Aa\x01 6`\x04a\x057V[a\x03\x81V[a\0\x95`\x01T\x81V[a\x01\xF4\x81\x11\x15a\x01rW`@Q\x7F\xE0\x82\x84\x0B\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x81\x01\x82\x90R`$\x01[`@Q\x80\x91\x03\x90\xFD[`\x01T`\0[\x82\x81\x10\x15a\x02\xADW`\x01T\x84\x84\x83\x81\x81\x10a\x01\x95Wa\x01\x95a\x05\xBDV[\x90P`\x80\x02\x01`\0\x015\x14a\x02\0W\x83\x83\x82\x81\x81\x10a\x01\xB6Wa\x01\xB6a\x05\xBDV[\x90P`\x80\x02\x01`\0\x015`\x01T`@Q\x7F4\xE4#\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01a\x01i\x92\x91\x90\x91\x82R` \x82\x01R`@\x01\x90V[a\x02\x1E\x84\x84\x83\x81\x81\x10a\x02\x15Wa\x02\x15a\x05\xBDV[\x90PPP`\x01\x90V[a\x02ZW`\x01T`@Q\x7Fx\x18g\x19\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01a\x01i\x91\x81R` \x01\x90V[\x83\x83\x82\x81\x81\x10a\x02lWa\x02la\x05\xBDV[\x90P`\x80\x02\x01` \x015`\0\x80`\x01T\x81R` \x01\x90\x81R` \x01`\0 \x81\x90UP`\x01\x80`\0\x82\x82Ta\x02\xA0\x91\x90a\x05\xD3V[\x90\x91UPP`\x01\x01a\x01xV[P`@\x80Q\x82\x81R` \x81\x01\x84\x90R\x7F\x82\x03\xA2\x1EO\x95\xF7.P\x81\xD5\xE0\x92\x9B\x1A\x8CR\x14\x1E\x12?\x9A\x14\xE1\xE7K\x02`\xFA_R\xF1\x91\x01[`@Q\x80\x91\x03\x90\xA1PPPV[a\x03\x18`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`\0`\x03\x83\x81T\x81\x10a\x03-Wa\x03-a\x05\xBDV[`\0\x91\x82R` \x80\x83 \x95\x83R`\x02\x80\x82R`@\x93\x84\x90 T\x84Q`\x80\x81\x01\x86R`\x04\x90\x94\x02\x90\x97\x01\x80T\x84R`\x01\x81\x01T\x92\x84\x01\x92\x90\x92R\x81\x01T\x92\x82\x01\x92\x90\x92R`\x03\x90\x91\x01T``\x82\x01R\x93\x91PPV[`\x03\x80T`\0\x81\x81R`\x02` \x90\x81R`@\x80\x83 \x86\x90U\x84T`\x01\x81\x01\x86U\x94\x90\x92R\x85Q`\x04\x90\x94\x02\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8[\x81\x01\x85\x90U\x86\x82\x01\x80Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8\\\x83\x01U\x87\x84\x01\x80Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8]\x84\x01U``\x80\x8A\x01\x80Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8^\x90\x95\x01\x94\x90\x94U\x85Q\x97\x88R\x91Q\x93\x87\x01\x93\x90\x93R\x91Q\x92\x85\x01\x92\x90\x92R\x90Q\x90\x83\x01R`\x80\x82\x01\x83\x90R`\xA0\x82\x01\x81\x90R\x90\x7F\xD7/\xE1\xACW\xD3\xE6\xD5\x1C\x92*\xE4\xD8\x11\xCCP\xAA:\xD7\x02b\x83\xAE\xA67IJ\x072RVZ\x90`\xC0\x01a\x02\xE0V[`\0\x80` \x83\x85\x03\x12\x15a\x04\xBCW`\0\x80\xFD[\x825g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x11\x15a\x04\xD4W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\x04\xE8W`\0\x80\xFD[\x815\x81\x81\x11\x15a\x04\xF7W`\0\x80\xFD[\x86` \x82`\x07\x1B\x85\x01\x01\x11\x15a\x05\x0CW`\0\x80\xFD[` \x92\x90\x92\x01\x96\x91\x95P\x90\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x050W`\0\x80\xFD[P5\x91\x90PV[`\0\x80\x82\x84\x03`\xA0\x81\x12\x15a\x05KW`\0\x80\xFD[`\x80\x81\x12\x15a\x05YW`\0\x80\xFD[P`@Q`\x80\x81\x01\x81\x81\x10g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x82\x11\x17\x15a\x05\x8BWcNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x90\x81R\x845\x82R` \x80\x86\x015\x90\x83\x01R\x84\x81\x015\x90\x82\x01R``\x80\x85\x015\x90\x82\x01R\x94`\x80\x90\x93\x015\x93PPPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x80\x82\x01\x80\x82\x11\x15a\x05\xF4WcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x92\x91PPV\xFE\xA1dsolcC\0\x08\x17\0\n"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[Pa\x05\xB8\x80a\0 `\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0bW`\x005`\xE0\x1C\x80c\n2\x1C\xFF\x14a\0gW\x80c&\x83=\xCC\x14a\0|W\x80cI\xCE\x89\x97\x14a\0\x98W\x80cg\xA2\x1Ep\x14a\0\xB8W\x80c\xF1\xF4]\x99\x14a\0\xD9W\x80c\xF4O\xF7\x12\x14a\0\xECW[`\0\x80\xFD[a\0za\0u6`\x04a\x03\xFCV[a\0\xF5V[\0[a\0\x85a\x01\xF4\x81V[`@Q\x90\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\x85a\0\xA66`\x04a\x04pV[`\0` \x81\x90R\x90\x81R`@\x90 T\x81V[a\0\xCBa\0\xC66`\x04a\x04pV[a\x02iV[`@Qa\0\x8F\x92\x91\x90a\x04\xAFV[a\0za\0\xE76`\x04a\x04\xCAV[a\x02\xFDV[a\0\x85`\x01T\x81V[a\x01\xF4\x81\x11\x15a\x01 W`@Qc\xE0\x82\x84\x0B`\xE0\x1B\x81R`\x04\x81\x01\x82\x90R`$\x01[`@Q\x80\x91\x03\x90\xFD[`\x01T`\0[\x82\x81\x10\x15a\x02)W`\x01T\x84\x84\x83\x81\x81\x10a\x01CWa\x01Ca\x05OV[\x90P`\x80\x02\x01`\0\x015\x14a\x01\x95W\x83\x83\x82\x81\x81\x10a\x01dWa\x01da\x05OV[\x90P`\x80\x02\x01`\0\x015`\x01T`@Qc4\xE4#\xFF`\xE0\x1B\x81R`\x04\x01a\x01\x17\x92\x91\x90\x91\x82R` \x82\x01R`@\x01\x90V[a\x01\xB3\x84\x84\x83\x81\x81\x10a\x01\xAAWa\x01\xAAa\x05OV[\x90PPP`\x01\x90V[a\x01\xD6W`\x01T`@Qcx\x18g\x19`\xE0\x1B\x81R`\x04\x01a\x01\x17\x91\x81R` \x01\x90V[\x83\x83\x82\x81\x81\x10a\x01\xE8Wa\x01\xE8a\x05OV[\x90P`\x80\x02\x01` \x015`\0\x80`\x01T\x81R` \x01\x90\x81R` \x01`\0 \x81\x90UP`\x01\x80`\0\x82\x82Ta\x02\x1C\x91\x90a\x05eV[\x90\x91UPP`\x01\x01a\x01&V[P`@\x80Q\x82\x81R` \x81\x01\x84\x90R\x7F\x82\x03\xA2\x1EO\x95\xF7.P\x81\xD5\xE0\x92\x9B\x1A\x8CR\x14\x1E\x12?\x9A\x14\xE1\xE7K\x02`\xFA_R\xF1\x91\x01[`@Q\x80\x91\x03\x90\xA1PPPV[a\x02\x94`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`\0`\x03\x83\x81T\x81\x10a\x02\xA9Wa\x02\xA9a\x05OV[`\0\x91\x82R` \x80\x83 \x95\x83R`\x02\x80\x82R`@\x93\x84\x90 T\x84Q`\x80\x81\x01\x86R`\x04\x90\x94\x02\x90\x97\x01\x80T\x84R`\x01\x81\x01T\x92\x84\x01\x92\x90\x92R\x81\x01T\x92\x82\x01\x92\x90\x92R`\x03\x90\x91\x01T``\x82\x01R\x93\x91PPV[`\x03\x80T`\0\x81\x81R`\x02` \x90\x81R`@\x80\x83 \x86\x90U\x84T`\x01\x81\x01\x86U\x94\x90\x92R\x85Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8[`\x04\x90\x95\x02\x94\x85\x01U\x85\x01Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8\\\x84\x01U\x84\x81\x01Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8]\x84\x01U``\x85\x01Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8^\x90\x93\x01\x92\x90\x92U\x90Q\x7F\xD7/\xE1\xACW\xD3\xE6\xD5\x1C\x92*\xE4\xD8\x11\xCCP\xAA:\xD7\x02b\x83\xAE\xA67IJ\x072RVZ\x90a\x02\\\x90\x85\x90\x85\x90\x85\x90a\x05\x8CV[`\0\x80` \x83\x85\x03\x12\x15a\x04\x0FW`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x80\x82\x11\x15a\x04&W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\x04:W`\0\x80\xFD[\x815\x81\x81\x11\x15a\x04IW`\0\x80\xFD[\x86` \x82`\x07\x1B\x85\x01\x01\x11\x15a\x04^W`\0\x80\xFD[` \x92\x90\x92\x01\x96\x91\x95P\x90\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x04\x82W`\0\x80\xFD[P5\x91\x90PV[\x80Q\x82R` \x81\x01Q` \x83\x01R`@\x81\x01Q`@\x83\x01R``\x81\x01Q``\x83\x01RPPV[`\xA0\x81\x01a\x04\xBD\x82\x85a\x04\x89V[\x82`\x80\x83\x01R\x93\x92PPPV[`\0\x80\x82\x84\x03`\xA0\x81\x12\x15a\x04\xDEW`\0\x80\xFD[`\x80\x81\x12\x15a\x04\xECW`\0\x80\xFD[P`@Q`\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x05\x1DWcNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x90\x81R\x845\x82R` \x80\x86\x015\x90\x83\x01R\x84\x81\x015\x90\x82\x01R``\x80\x85\x015\x90\x82\x01R\x94`\x80\x90\x93\x015\x93PPPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x80\x82\x01\x80\x82\x11\x15a\x05\x86WcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x92\x91PPV[`\xC0\x81\x01a\x05\x9A\x82\x86a\x04\x89V[`\x80\x82\x01\x93\x90\x93R`\xA0\x01R\x91\x90PV\xFE\xA1dsolcC\0\x08\x17\0\n"; /// The bytecode of the contract. pub static HOTSHOT_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(__BYTECODE); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0rW`\x005`\xE0\x1C\x80cg\xA2\x1Ep\x11a\0PW\x80cg\xA2\x1Ep\x14a\0\xC8W\x80c\xF1\xF4]\x99\x14a\x01\x12W\x80c\xF4O\xF7\x12\x14a\x01%W`\0\x80\xFD[\x80c\n2\x1C\xFF\x14a\0wW\x80c&\x83=\xCC\x14a\0\x8CW\x80cI\xCE\x89\x97\x14a\0\xA8W[`\0\x80\xFD[a\0\x8Aa\0\x856`\x04a\x04\xA9V[a\x01.V[\0[a\0\x95a\x01\xF4\x81V[`@Q\x90\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\x95a\0\xB66`\x04a\x05\x1EV[`\0` \x81\x90R\x90\x81R`@\x90 T\x81V[a\0\xDBa\0\xD66`\x04a\x05\x1EV[a\x02\xEDV[`@\x80Q\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R\x83\x82\x01Q\x91\x81\x01\x91\x90\x91R``\x92\x83\x01Q\x92\x81\x01\x92\x90\x92R`\x80\x82\x01R`\xA0\x01a\0\x9FV[a\0\x8Aa\x01 6`\x04a\x057V[a\x03\x81V[a\0\x95`\x01T\x81V[a\x01\xF4\x81\x11\x15a\x01rW`@Q\x7F\xE0\x82\x84\x0B\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x81\x01\x82\x90R`$\x01[`@Q\x80\x91\x03\x90\xFD[`\x01T`\0[\x82\x81\x10\x15a\x02\xADW`\x01T\x84\x84\x83\x81\x81\x10a\x01\x95Wa\x01\x95a\x05\xBDV[\x90P`\x80\x02\x01`\0\x015\x14a\x02\0W\x83\x83\x82\x81\x81\x10a\x01\xB6Wa\x01\xB6a\x05\xBDV[\x90P`\x80\x02\x01`\0\x015`\x01T`@Q\x7F4\xE4#\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01a\x01i\x92\x91\x90\x91\x82R` \x82\x01R`@\x01\x90V[a\x02\x1E\x84\x84\x83\x81\x81\x10a\x02\x15Wa\x02\x15a\x05\xBDV[\x90PPP`\x01\x90V[a\x02ZW`\x01T`@Q\x7Fx\x18g\x19\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01a\x01i\x91\x81R` \x01\x90V[\x83\x83\x82\x81\x81\x10a\x02lWa\x02la\x05\xBDV[\x90P`\x80\x02\x01` \x015`\0\x80`\x01T\x81R` \x01\x90\x81R` \x01`\0 \x81\x90UP`\x01\x80`\0\x82\x82Ta\x02\xA0\x91\x90a\x05\xD3V[\x90\x91UPP`\x01\x01a\x01xV[P`@\x80Q\x82\x81R` \x81\x01\x84\x90R\x7F\x82\x03\xA2\x1EO\x95\xF7.P\x81\xD5\xE0\x92\x9B\x1A\x8CR\x14\x1E\x12?\x9A\x14\xE1\xE7K\x02`\xFA_R\xF1\x91\x01[`@Q\x80\x91\x03\x90\xA1PPPV[a\x03\x18`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`\0`\x03\x83\x81T\x81\x10a\x03-Wa\x03-a\x05\xBDV[`\0\x91\x82R` \x80\x83 \x95\x83R`\x02\x80\x82R`@\x93\x84\x90 T\x84Q`\x80\x81\x01\x86R`\x04\x90\x94\x02\x90\x97\x01\x80T\x84R`\x01\x81\x01T\x92\x84\x01\x92\x90\x92R\x81\x01T\x92\x82\x01\x92\x90\x92R`\x03\x90\x91\x01T``\x82\x01R\x93\x91PPV[`\x03\x80T`\0\x81\x81R`\x02` \x90\x81R`@\x80\x83 \x86\x90U\x84T`\x01\x81\x01\x86U\x94\x90\x92R\x85Q`\x04\x90\x94\x02\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8[\x81\x01\x85\x90U\x86\x82\x01\x80Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8\\\x83\x01U\x87\x84\x01\x80Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8]\x84\x01U``\x80\x8A\x01\x80Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8^\x90\x95\x01\x94\x90\x94U\x85Q\x97\x88R\x91Q\x93\x87\x01\x93\x90\x93R\x91Q\x92\x85\x01\x92\x90\x92R\x90Q\x90\x83\x01R`\x80\x82\x01\x83\x90R`\xA0\x82\x01\x81\x90R\x90\x7F\xD7/\xE1\xACW\xD3\xE6\xD5\x1C\x92*\xE4\xD8\x11\xCCP\xAA:\xD7\x02b\x83\xAE\xA67IJ\x072RVZ\x90`\xC0\x01a\x02\xE0V[`\0\x80` \x83\x85\x03\x12\x15a\x04\xBCW`\0\x80\xFD[\x825g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x11\x15a\x04\xD4W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\x04\xE8W`\0\x80\xFD[\x815\x81\x81\x11\x15a\x04\xF7W`\0\x80\xFD[\x86` \x82`\x07\x1B\x85\x01\x01\x11\x15a\x05\x0CW`\0\x80\xFD[` \x92\x90\x92\x01\x96\x91\x95P\x90\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x050W`\0\x80\xFD[P5\x91\x90PV[`\0\x80\x82\x84\x03`\xA0\x81\x12\x15a\x05KW`\0\x80\xFD[`\x80\x81\x12\x15a\x05YW`\0\x80\xFD[P`@Q`\x80\x81\x01\x81\x81\x10g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x82\x11\x17\x15a\x05\x8BWcNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x90\x81R\x845\x82R` \x80\x86\x015\x90\x83\x01R\x84\x81\x015\x90\x82\x01R``\x80\x85\x015\x90\x82\x01R\x94`\x80\x90\x93\x015\x93PPPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x80\x82\x01\x80\x82\x11\x15a\x05\xF4WcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x92\x91PPV\xFE\xA1dsolcC\0\x08\x17\0\n"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0bW`\x005`\xE0\x1C\x80c\n2\x1C\xFF\x14a\0gW\x80c&\x83=\xCC\x14a\0|W\x80cI\xCE\x89\x97\x14a\0\x98W\x80cg\xA2\x1Ep\x14a\0\xB8W\x80c\xF1\xF4]\x99\x14a\0\xD9W\x80c\xF4O\xF7\x12\x14a\0\xECW[`\0\x80\xFD[a\0za\0u6`\x04a\x03\xFCV[a\0\xF5V[\0[a\0\x85a\x01\xF4\x81V[`@Q\x90\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\x85a\0\xA66`\x04a\x04pV[`\0` \x81\x90R\x90\x81R`@\x90 T\x81V[a\0\xCBa\0\xC66`\x04a\x04pV[a\x02iV[`@Qa\0\x8F\x92\x91\x90a\x04\xAFV[a\0za\0\xE76`\x04a\x04\xCAV[a\x02\xFDV[a\0\x85`\x01T\x81V[a\x01\xF4\x81\x11\x15a\x01 W`@Qc\xE0\x82\x84\x0B`\xE0\x1B\x81R`\x04\x81\x01\x82\x90R`$\x01[`@Q\x80\x91\x03\x90\xFD[`\x01T`\0[\x82\x81\x10\x15a\x02)W`\x01T\x84\x84\x83\x81\x81\x10a\x01CWa\x01Ca\x05OV[\x90P`\x80\x02\x01`\0\x015\x14a\x01\x95W\x83\x83\x82\x81\x81\x10a\x01dWa\x01da\x05OV[\x90P`\x80\x02\x01`\0\x015`\x01T`@Qc4\xE4#\xFF`\xE0\x1B\x81R`\x04\x01a\x01\x17\x92\x91\x90\x91\x82R` \x82\x01R`@\x01\x90V[a\x01\xB3\x84\x84\x83\x81\x81\x10a\x01\xAAWa\x01\xAAa\x05OV[\x90PPP`\x01\x90V[a\x01\xD6W`\x01T`@Qcx\x18g\x19`\xE0\x1B\x81R`\x04\x01a\x01\x17\x91\x81R` \x01\x90V[\x83\x83\x82\x81\x81\x10a\x01\xE8Wa\x01\xE8a\x05OV[\x90P`\x80\x02\x01` \x015`\0\x80`\x01T\x81R` \x01\x90\x81R` \x01`\0 \x81\x90UP`\x01\x80`\0\x82\x82Ta\x02\x1C\x91\x90a\x05eV[\x90\x91UPP`\x01\x01a\x01&V[P`@\x80Q\x82\x81R` \x81\x01\x84\x90R\x7F\x82\x03\xA2\x1EO\x95\xF7.P\x81\xD5\xE0\x92\x9B\x1A\x8CR\x14\x1E\x12?\x9A\x14\xE1\xE7K\x02`\xFA_R\xF1\x91\x01[`@Q\x80\x91\x03\x90\xA1PPPV[a\x02\x94`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`\0`\x03\x83\x81T\x81\x10a\x02\xA9Wa\x02\xA9a\x05OV[`\0\x91\x82R` \x80\x83 \x95\x83R`\x02\x80\x82R`@\x93\x84\x90 T\x84Q`\x80\x81\x01\x86R`\x04\x90\x94\x02\x90\x97\x01\x80T\x84R`\x01\x81\x01T\x92\x84\x01\x92\x90\x92R\x81\x01T\x92\x82\x01\x92\x90\x92R`\x03\x90\x91\x01T``\x82\x01R\x93\x91PPV[`\x03\x80T`\0\x81\x81R`\x02` \x90\x81R`@\x80\x83 \x86\x90U\x84T`\x01\x81\x01\x86U\x94\x90\x92R\x85Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8[`\x04\x90\x95\x02\x94\x85\x01U\x85\x01Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8\\\x84\x01U\x84\x81\x01Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8]\x84\x01U``\x85\x01Q\x7F\xC2WZ\x0E\x9EY<\0\xF9Y\xF8\xC9/\x12\xDB(i\xC39Z;\x05\x02\xD0^%\x16Doq\xF8^\x90\x93\x01\x92\x90\x92U\x90Q\x7F\xD7/\xE1\xACW\xD3\xE6\xD5\x1C\x92*\xE4\xD8\x11\xCCP\xAA:\xD7\x02b\x83\xAE\xA67IJ\x072RVZ\x90a\x02\\\x90\x85\x90\x85\x90\x85\x90a\x05\x8CV[`\0\x80` \x83\x85\x03\x12\x15a\x04\x0FW`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x80\x82\x11\x15a\x04&W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12a\x04:W`\0\x80\xFD[\x815\x81\x81\x11\x15a\x04IW`\0\x80\xFD[\x86` \x82`\x07\x1B\x85\x01\x01\x11\x15a\x04^W`\0\x80\xFD[` \x92\x90\x92\x01\x96\x91\x95P\x90\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x04\x82W`\0\x80\xFD[P5\x91\x90PV[\x80Q\x82R` \x81\x01Q` \x83\x01R`@\x81\x01Q`@\x83\x01R``\x81\x01Q``\x83\x01RPPV[`\xA0\x81\x01a\x04\xBD\x82\x85a\x04\x89V[\x82`\x80\x83\x01R\x93\x92PPPV[`\0\x80\x82\x84\x03`\xA0\x81\x12\x15a\x04\xDEW`\0\x80\xFD[`\x80\x81\x12\x15a\x04\xECW`\0\x80\xFD[P`@Q`\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x05\x1DWcNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x90\x81R\x845\x82R` \x80\x86\x015\x90\x83\x01R\x84\x81\x015\x90\x82\x01R``\x80\x85\x015\x90\x82\x01R\x94`\x80\x90\x93\x015\x93PPPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x80\x82\x01\x80\x82\x11\x15a\x05\x86WcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x92\x91PPV[`\xC0\x81\x01a\x05\x9A\x82\x86a\x04\x89V[`\x80\x82\x01\x93\x90\x93R`\xA0\x01R\x91\x90PV\xFE\xA1dsolcC\0\x08\x17\0\n"; /// The deployed bytecode of the contract. pub static HOTSHOT_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(__DEPLOYED_BYTECODE); diff --git a/contract-bindings/src/light_client.rs b/contract-bindings/src/light_client.rs index be5eb220c..6d3c78ba2 100644 --- a/contract-bindings/src/light_client.rs +++ b/contract-bindings/src/light_client.rs @@ -195,6 +195,65 @@ pub mod light_client { state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, },], ), + ( + ::std::borrow::ToOwned::to_owned("getHotShotBlockCommitmentsCount"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("getHotShotBlockCommitmentsCount",), + inputs: ::std::vec![], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), + ( + ::std::borrow::ToOwned::to_owned("getHotShotCommitment"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("getHotShotCommitment",), + inputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("hotShotBlockHeight",), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Tuple(::std::vec![ + ::ethers::core::abi::ethabi::ParamType::Uint(64usize), + ::ethers::core::abi::ethabi::ParamType::Uint(256usize), + ],), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned( + "struct LightClient.HotShotCommitment", + ), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), + ( + ::std::borrow::ToOwned::to_owned("getStateUpdateBlockNumbersCount"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("getStateUpdateBlockNumbersCount",), + inputs: ::std::vec![], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), ( ::std::borrow::ToOwned::to_owned("getVersion"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -227,6 +286,37 @@ pub mod light_client { state_mutability: ::ethers::core::abi::ethabi::StateMutability::Pure, },], ), + ( + ::std::borrow::ToOwned::to_owned("hotShotCommitments"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("hotShotCommitments"), + inputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + outputs: ::std::vec![ + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("blockHeight"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(64usize), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint64"), + ), + }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("blockCommRoot"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("BN254.ScalarField"), + ), + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), ( ::std::borrow::ToOwned::to_owned("initialize"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -270,6 +360,37 @@ pub mod light_client { state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, },], ), + ( + ::std::borrow::ToOwned::to_owned("lagOverEscapeHatchThreshold"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("lagOverEscapeHatchThreshold",), + inputs: ::std::vec![ + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("blockNumber"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("threshold"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + }, + ], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Bool, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("bool"), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), ( ::std::borrow::ToOwned::to_owned("newFinalizedState"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -461,6 +582,28 @@ pub mod light_client { state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, },], ), + ( + ::std::borrow::ToOwned::to_owned("stateUpdateBlockNumbers"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("stateUpdateBlockNumbers",), + inputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), ( ::std::borrow::ToOwned::to_owned("states"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -762,6 +905,13 @@ pub mod light_client { inputs: ::std::vec![], },], ), + ( + ::std::borrow::ToOwned::to_owned("InsufficientSnapshotHistory"), + ::std::vec![::ethers::core::abi::ethabi::AbiError { + name: ::std::borrow::ToOwned::to_owned("InsufficientSnapshotHistory",), + inputs: ::std::vec![], + },], + ), ( ::std::borrow::ToOwned::to_owned("InvalidAddress"), ::std::vec![::ethers::core::abi::ethabi::AbiError { @@ -776,6 +926,15 @@ pub mod light_client { inputs: ::std::vec![], },], ), + ( + ::std::borrow::ToOwned::to_owned("InvalidHotShotBlockForCommitmentCheck"), + ::std::vec![::ethers::core::abi::ethabi::AbiError { + name: ::std::borrow::ToOwned::to_owned( + "InvalidHotShotBlockForCommitmentCheck", + ), + inputs: ::std::vec![], + },], + ), ( ::std::borrow::ToOwned::to_owned("InvalidInitialization"), ::std::vec![::ethers::core::abi::ethabi::AbiError { @@ -921,12 +1080,12 @@ pub mod light_client { pub static LIGHTCLIENT_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\xA0`@R0`\x80R4\x80\x15b\0\0\x15W`\0\x80\xFD[Pb\0\0 b\0\0&V[b\0\0\xDAV[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15b\0\0wW`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80T`\x01`\x01`@\x1B\x03\x90\x81\x16\x14b\0\0\xD7W\x80T`\x01`\x01`@\x1B\x03\x19\x16`\x01`\x01`@\x1B\x03\x90\x81\x17\x82U`@Q\x90\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PV[`\x80Qa]\x9Bb\0\x01\x04`\09`\0\x81\x81a\x13\xD0\x01R\x81\x81a\x13\xF9\x01Ra\x15\xE7\x01Ra]\x9B`\0\xF3\xFE`\x80`@R`\x046\x10a\x01\x80W`\x005`\xE0\x1C\x80cvg\x18\x08\x11a\0\xD6W\x80c\xAA\x92'2\x11a\0\x7FW\x80c\xCAo\xE8U\x11a\0YW\x80c\xCAo\xE8U\x14a\x05\xC8W\x80c\xF0h T\x14a\x05\xDEW\x80c\xF2\xFD\xE3\x8B\x14a\x06\x10W`\0\x80\xFD[\x80c\xAA\x92'2\x14a\x04\xCDW\x80c\xAD<\xB1\xCC\x14a\x050W\x80c\xBD2Q\x9A\x14a\x05\x86W`\0\x80\xFD[\x80c\x82\xD0\x7F\xF3\x11a\0\xB0W\x80c\x82\xD0\x7F\xF3\x14a\x04[W\x80c\x8D\xA5\xCB[\x14a\x04pW\x80c\xA2D\xD5\x96\x14a\x04\xADW`\0\x80\xFD[\x80cvg\x18\x08\x14a\x03AW\x80cv\xB6\xB7\xCB\x14a\x03\x8BW\x80c\x7F\x17\xBA\xAD\x14a\x03\xA1W`\0\x80\xFD[\x80cHG\xAE]\x11a\x018W\x80cb\x82w3\x11a\x01\x12W\x80cb\x82w3\x14a\x03\x01W\x80ci\xCCj\x04\x14a\x03\x17W\x80cqP\x18\xA6\x14a\x03,W`\0\x80\xFD[\x80cHG\xAE]\x14a\x02UW\x80cO\x1E\xF2\x86\x14a\x02\xD9W\x80cR\xD1\x90-\x14a\x02\xECW`\0\x80\xFD[\x80c1=\xF7\xB1\x11a\x01iW\x80c1=\xF7\xB1\x14a\x01\xD9W\x80c8+!Z\x14a\x02\x11W\x80c@\x999\xB7\x14a\x025W`\0\x80\xFD[\x80c\x01?\xA5\xFC\x14a\x01\x85W\x80c\r\x8En,\x14a\x01\xA7W[`\0\x80\xFD[4\x80\x15a\x01\x91W`\0\x80\xFD[Pa\x01\xA5a\x01\xA06`\x04aT\x87V[a\x060V[\0[4\x80\x15a\x01\xB3W`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\xE5W`\0\x80\xFD[P`\x06Ta\x01\xF9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xD0V[4\x80\x15a\x02\x1DW`\0\x80\xFD[Pa\x02'`\x03T\x81V[`@Q\x90\x81R` \x01a\x01\xD0V[4\x80\x15a\x02AW`\0\x80\xFD[Pa\x01\xA5a\x02P6`\x04aV\x18V[a\x07HV[4\x80\x15a\x02aW`\0\x80\xFD[Pa\x02ja\n\x8AV[`@Qa\x01\xD0\x91\x90`\0a\x01\0\x82\x01\x90Pg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x84Q\x16\x83R\x80` \x85\x01Q\x16` \x84\x01RP`@\x83\x01Q`@\x83\x01R``\x83\x01Q``\x83\x01R`\x80\x83\x01Q`\x80\x83\x01R`\xA0\x83\x01Q`\xA0\x83\x01R`\xC0\x83\x01Q`\xC0\x83\x01R`\xE0\x83\x01Q`\xE0\x83\x01R\x92\x91PPV[a\x01\xA5a\x02\xE76`\x04aW\xF0V[a\x0BwV[4\x80\x15a\x02\xF8W`\0\x80\xFD[Pa\x02'a\x0B\x96V[4\x80\x15a\x03\rW`\0\x80\xFD[Pa\x02'`\x02T\x81V[4\x80\x15a\x03#W`\0\x80\xFD[Pa\x01\xA5a\x0B\xC5V[4\x80\x15a\x038W`\0\x80\xFD[Pa\x01\xA5a\x0CwV[4\x80\x15a\x03MW`\0\x80\xFD[P`\0Ta\x03r\x90l\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81V[`@Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x91\x16\x81R` \x01a\x01\xD0V[4\x80\x15a\x03\x97W`\0\x80\xFD[Pa\x02'`\x01T\x81V[4\x80\x15a\x03\xADW`\0\x80\xFD[Pa\x04\x14a\x03\xBC6`\x04aX\xAAV[`\x05` \x81\x90R`\0\x91\x82R`@\x90\x91 \x80T`\x01\x82\x01T`\x02\x83\x01T`\x03\x84\x01T`\x04\x85\x01T\x95\x85\x01T`\x06\x90\x95\x01Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x86\x16\x97h\x01\0\0\0\0\0\0\0\0\x90\x96\x04\x16\x95\x93\x94\x92\x93\x91\x92\x91\x90\x88V[`@\x80Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x99\x8A\x16\x81R\x98\x90\x97\x16` \x89\x01R\x95\x87\x01\x94\x90\x94R``\x86\x01\x92\x90\x92R`\x80\x85\x01R`\xA0\x84\x01R`\xC0\x83\x01R`\xE0\x82\x01Ra\x01\0\x01a\x01\xD0V[4\x80\x15a\x04gW`\0\x80\xFD[Pa\x02ja\x0C\x89V[4\x80\x15a\x04|W`\0\x80\xFD[P\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`\x01`\x01`\xA0\x1B\x03\x16a\x01\xF9V[4\x80\x15a\x04\xB9W`\0\x80\xFD[Pa\x01\xA5a\x04\xC86`\x04aX\xC5V[a\rsV[4\x80\x15a\x04\xD9W`\0\x80\xFD[Pa\x02'a\x04\xE86`\x04aY\x0CV[`\x80\x80\x82\x01Q`\xA0\x83\x01Q`\xC0\x84\x01Q`@\x80Q` \x81\x01\x94\x90\x94R\x83\x01\x91\x90\x91R``\x82\x01R`\0\x91\x01`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[4\x80\x15a\x05 v\xCC75\xA9 \xA3\xCAP]8+\xBC\x90V[a\x0B\xCDa\x0FeV[`\x06Tt\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15a\x0CCW`\x06\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90U`@Q\x7F\x9A_W\xDE\x85m\xD6h\xC5M\xD9^\\U\xDF\x93C!q\xCB\xCAI\xA8wmV \xEAY\xC0$P\x90`\0\x90\xA1V[`@Q\x7F\xA8c\xAE\xC9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[V[a\x0C\x7Fa\x0FeV[a\x0Cu`\0a\x16>V[a\x0C\xE5`@Q\x80a\x01\0\x01`@R\x80`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[P`\0\x80Tc\xFF\xFF\xFF\xFFh\x01\0\0\0\0\0\0\0\0\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x84Qa\x01\0\x81\x01\x86R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R\x95\x90\x04\x90\x94\x16\x91\x84\x01\x91\x90\x91R`\x01\x81\x01T\x93\x83\x01\x93\x90\x93R`\x02\x83\x01T``\x83\x01R`\x03\x83\x01T`\x80\x83\x01R`\x04\x83\x01T`\xA0\x83\x01R\x82\x01T`\xC0\x82\x01R`\x06\x90\x91\x01T`\xE0\x82\x01R\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x81\x04`\xFF\x16\x15\x90g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\0\x81\x15\x80\x15a\r\xBEWP\x82[\x90P`\0\x82g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\x01\x14\x80\x15a\r\xDBWP0;\x15[\x90P\x81\x15\x80\x15a\r\xE9WP\x80\x15[\x15a\x0E W`@Q\x7F\xF9.\xE8\xA9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\x16`\x01\x17\x85U\x83\x15a\x0EkW\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16h\x01\0\0\0\0\0\0\0\0\x17\x85U[a\x0Et\x86a\x16\xC7V[a\x0E|a\x16\xD8V[`\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\xFF\xFF\xFF\xFF\x16h\x01\0\0\0\0\0\0\0\0\x17\x90Ua\x0E\xB9\x88\x88a\x16\xE0V[\x83\x15a\x0F\x04W\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16\x85U`@Q`\x01\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPPPV[a\x0F\x16a\x0FeV[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x0FYW`@Q\x7F\x1EO\xBD\xF7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\0`\x04\x82\x01R`$\x01a\tBV[a\x0Fb\x81a\x16>V[PV[3a\x0F\x97\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\x0CuW`@Q\x7F\x11\x8C\xDA\xA7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R3`\x04\x82\x01R`$\x01a\tBV[\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x81\x10\x80a\x0B\x92W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1B`$\x82\x01R\x7FBn254: invalid scalar field\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[`\0\x80Th\x01\0\0\0\0\0\0\0\0\x80\x82\x04c\xFF\xFF\xFF\xFF\x16\x80\x84R`\x05` \x81\x81R`@\x80\x87 \x81Qa\x01\0\x81\x01\x83R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R\x97\x90\x04\x87\x16\x81\x85\x01R`\x01\x80\x83\x01T\x82\x85\x01R`\x02\x80\x84\x01T``\x80\x85\x01\x91\x90\x91R`\x03\x80\x86\x01T`\x80\x80\x87\x01\x82\x90R`\x04\x80\x89\x01T`\xA0\x89\x01\x81\x90R\x89\x8D\x01T`\xC0\x8A\x01\x81\x90R`\x06\x90\x9A\x01\x80T`\xE0\x90\x9A\x01\x99\x90\x99R\x8AQ\x80\x8D\x01\x94\x90\x94R\x83\x8B\x01R\x82\x85\x01\x98\x90\x98R\x88Q\x80\x83\x03\x90\x94\x01\x84R\x01\x90\x96R\x80Q\x90\x87\x01 \x85T\x83U\x94\x85\x90U\x83T\x90U\x95\x89R\x93\x90\x92R\x91T\x90U\x93\x90\x92\x90\x91`\x0C\x91a\x11C\x91\x85\x91l\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04\x16aY\xBAV[\x82Ta\x01\0\x92\x90\x92\ng\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x81\x02\x19\x90\x93\x16\x91\x83\x16\x02\x17\x90\x91U`\0T`@Ql\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x91\x04\x90\x91\x16\x81R\x7F\xDB5X%\x9E\x03\x9D~P\xE8\x16\xB9\xDC\xCE0\xFB\x11M\x8A\x9C\x86\xEC\xA5\xAB\x14\xB6\x01\x94\xD6\x94]?\x91P` \x01a\x07=V[`\0a\x11\xB5a\x19\xD0V[`@\x80Q`\x08\x80\x82Ra\x01 \x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x01\0\x806\x837\x01\x90PP\x90P`\x02T\x81`\0\x81Q\x81\x10a\x11\xF4Wa\x11\xF4aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x83`\0\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81`\x01\x81Q\x81\x10a\x12\"Wa\x12\"aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x83` \x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81`\x02\x81Q\x81\x10a\x12PWa\x12PaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x83`@\x01Q\x81`\x03\x81Q\x81\x10a\x12tWa\x12taY\xDBV[` \x02` \x01\x01\x81\x81RPP\x83``\x01Q\x81`\x04\x81Q\x81\x10a\x12\x98Wa\x12\x98aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 `\x03\x01T\x82Q\x90\x91\x83\x91\x81\x10a\x12\xE1Wa\x12\xE1aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x90\x91R`@\x90 `\x04\x01T\x81Q\x82\x90`\x06\x90\x81\x10a\x13*Wa\x13*aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 \x01T\x81Q\x82\x90`\x07\x90\x81\x10a\x13rWa\x13raY\xDBV[` \x02` \x01\x01\x81\x81RPPa\x13\x89\x82\x82\x85a\x1F\xB2V[a\x13\xBFW`@Q\x7F\t\xBD\xE39\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[PPPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14\x80a\x14^WP\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16a\x14R\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBCT`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14\x15[\x15a\x0CuW`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x14\x9Da\x0FeV[`@Q`\x01`\x01`\xA0\x1B\x03\x82\x16\x81R\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x90` \x01a\x07=V[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x150WP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x15-\x91\x81\x01\x90aY\xF1V[`\x01[a\x15qW`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16`\x04\x82\x01R`$\x01a\tBV[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x81\x14a\x15\xCDW`@Q\x7F\xAA\x1DI\xA4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x81\x01\x82\x90R`$\x01a\tBV[a\x15\xD7\x83\x83a \x9DV[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\x0CuW`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x16`\x01`\x01`\xA0\x1B\x03\x84\x81\x16\x91\x82\x17\x84U`@Q\x92\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[a\x16\xCFa \xF3V[a\x0Fb\x81a!ZV[a\x0Cua \xF3V[\x81Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x15\x15\x80a\x17\x06WP` \x82\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x15\x15[\x80a\x17\x13WP`\x80\x82\x01Q\x15[\x80a\x17 WP`\xA0\x82\x01Q\x15[\x80a\x17-WP`\xC0\x82\x01Q\x15[\x80a\x17:WP`\xE0\x82\x01Q\x15[\x80a\x17IWPc\xFF\xFF\xFF\xFF\x81\x16\x15[\x15a\x17\x80W`@Q\x7F\xA1\xBA\x07\xEE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x81`\x05`\0\x80`\x04\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP\x81`\x05`\0\x80`\x08\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP`\0\x80`\x0Ca\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP\x80`\0\x80a\x01\0\n\x81T\x81c\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83c\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`\0a\x19\xB3\x83`\x80\x80\x82\x01Q`\xA0\x83\x01Q`\xC0\x84\x01Q`@\x80Q` \x81\x01\x94\x90\x94R\x83\x01\x91\x90\x91R``\x82\x01R`\0\x91\x01`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[`\x01\x81\x90U`\xE0\x90\x93\x01Q`\x02\x81\x90U`\x03\x93\x90\x93UPP`\x04UV[a\x19\xD8aQcV[b\x10\0\0\x81R`\x08` \x82\x01R\x7F \xC9@13\xDF\xDE\x9A\x9D8-\xF7o\xB0R5qd\x87%\xAB\xC0\xA7\xC1(0\xBBi\x0E\xC8;3`@\x82\x01QR\x7F\x03\xA0\xA9\xAC\xC3\xE3\x81Z~\xD6\xCB\x13y\xF7\xD1W\xE641dr\x93v9*i:\xCB\xD3\xEC(<` `@\x83\x01Q\x01R\x7F(f\xC1\x8A\xD1\xDF\x10\xEF\x13T,\xCEbP\xCE\x02\xCB*kr\xAE\0\xA9\x85.'\x11\x87\xE9\xE4\xE0\xDB``\x82\x01QR\x7F!\xBE#*B$jVc\xEB\xF4\x83G\x0C\xCAfo\xFE\x9DO\x0Ec\xB9)\xC5\x96\xA7e\x87\x14\xE9p` ``\x83\x01Q\x01R\x7F\x07\xD7xs\xB9\x86\0t\x11\x8Eu\x80\x8CyF\x8B\x83\xC8\xEDd\xBA\x14\xDB\\\xB5\xAF\xA8\xE54\xDE{\x99`\x80\x82\x01QR\x7F\x0B\xE0\xF4H\x83\x90\x80\x13-G\xDE\x17\xDE\0\x99\xB4\xCDt\xAE\x1Ekq\xCD\xDA\x06\xCD\xEB\xB8h\xA5\x0Cm` `\x80\x83\x01Q\x01R\x7F\x13\xBDE\xA0#I\x1E\xAD\xEAD\xCC?$\xCF\xBD\x17\x96\xEA\xDE\x9C\x0E9\xEE\x81\xD9\xF6>\xA0\xA5\x80f%`\xA0\x82\x01QR\x7F\x18\xF9\\\xDD\xA4,\xE1\x1D\x9D\x10\xA3\xB35\xAC\xC2\x14\xE3\x80|W\x8CSY@]\x81\x0C \x8D\xF6\0\x93` `\xA0\x83\x01Q\x01R\x7F\tp\xD9xv4a\xF0\x9E\x9E\xC64T\x074\x978nM(/\xED\xC2\xAC[\x96|\xB9\xFD?\xA8\xA9`\xC0\x82\x01QR\x7F(\xC2!\x7F{\xAC\xF6\xF8\xB2\xB8\xEEJ\x90\xFC\xF8\xB5\xBC\xA0B\x05\xEA\x84\xE8\xE1\xEBT\xB8]\xD4\x1B\xDE(` `\xC0\x83\x01Q\x01R\x7F\x02\xFE=\x02\x98\x8D\xB7\x188\0R\x97\n\xBAF\xA3)m\xF5\xF2\x9Bsk\xA1\xF2\xC4\xCC\xFF\xC8\xB5\x96\x93`\xE0\x82\x01QR\x7F ,>9\x0C\xEE|\\\x85%\xDA#)\xA1\x9FI6\xF6\xF7\x1C\xA9}\xDElo\xA3+8-Z\xCC\x03` `\xE0\x83\x01Q\x01R\x7F#\xAC\x10\xAEl\xA5\xCA\xCE\xE8tK\xB99\xAA\xA859\tT\xB9\x1A\xE6h\xA2\xC8\xD0\xED\xDAU\x8A\x89\xE7a\x01\0\x82\x01QR\x7F\x1C\x8C+\x85l\xDA\xDE%k\xA3#\x7F9\xAF\xD5\xE1p\xA9S \x12\xF7\xAE\xCA\xE4\x9DE\x9B)\xF6\xF6\xAD` a\x01\0\x83\x01Q\x01R\x7F\x16\xEC\x03\xD2`\xBDz\xC1\xC5\x0F\xFAcV]Rt\xB4X,\xEE\xA5/\xF4\x0B\x81\xCD\xFE\x8FDO\x01\xE4a\x01 \x82\x01QR\x7F)9!Rr0\x97\xE0q\x13\xC3\xD7xm$^\xC4\x0C0\x92\x80\x15\xCDP\xB5f\x8AON\xA1p1` a\x01 \x83\x01Q\x01R\x7F,\xDB\xFB:@S\xC8H\x9B\x0C\x94\xE7C8\xAC\x19\x11\x8D\xF7\xA0k\xC5k\x1E\xB4\xD0\xE0\xDCN\xAErHa\x01@\x82\x01QR\x7F\x07\xFE\xA1'\xDA\xE9C\xB8\xDC\x14\x8F\x14\x08\xD4\x0C\xFFF\\\x9CG!\x946i\xB1\xE4\xFDZ9\xDBp6` a\x01@\x83\x01Q\x01R\x7F\x03\x14U\xA7\x9A.\x0C\xE7\x8Al\xB55&\xEC\x04\xAC\x19qj\x86\xB0\x8A\x93\xDFH\xD1x\xF8\xB7~V\x19a\x01`\x82\x01QR\x7F\x11\x86#\xE6\xBC\x13n\xE6\xD3\xF9\x90|\xD4\xAD\x04\xA9A\x8E\xA0;\xA9\x9A\xD7S\"|\xDF\xEEY\x8E\x84\x15` a\x01`\x83\x01Q\x01R\x7F\x08a\xD1\x99wa\xA8R\"j\xAC{\xA9q{\xF6\xAEVE\x10\x99\xBEwL\xDF\x02\xEF5*X\xCB\xC8a\x01\x80\x82\x01QR\x7F\x08\x05\xE3\x92\xBC\xBC\x12\xE4\nr'xc-s\xFE\x98\x1EK\xC6\xFAm\x11x\xB7\n\xF7\xBE\x1C\xB9\xA3\xA3` a\x01\x80\x83\x01Q\x01R\x7F\x10\x1D\x1E9x\xCB\x9F\x1E0=A1D\xEB\xE6v\x82\xC9\xEB\x0C\xFE\x11$)Y\xAA`)\xD7\x8C\xDB\xBCa\x01\xA0\x82\x01QR\x7F\x08\x9E\xB9\xC7'\xE6\xCB\x07\x08+\xC3\xE6\xF4\x0C\xF0OC\x9F\xE4\x80\0`+XGt\xDA\xD7\xEF\xC6`|` a\x01\xA0\x83\x01Q\x01R\x7F-H\x9F$\x93&:\xA8s\xBC\xD9O!\xEF\xB4[\xF2W\xA6\x1D\x81\xC0\xC9\\2\x97\x91e\x06e;@a\x01\xC0\x82\x01QR\x7F\x18\xE4]bz\xAD\xD4\xDF'\x94\xEC\xD9\x90\x9F\xAC\x1Au?\x0Co\xA8\xA9\xC6eJzX\xB0\x91/\xFF\xD5` a\x01\xC0\x83\x01Q\x01R\x7F\x0EC\xE3\xA4\xB1<\xB48\xE2\xAD\x92F\x14&\x1A\xD0$\x02\x14\xFA\x1C\x83\xFC\xDAj\x0B\xF7y\xEB9\xFF\xC5a\x01\xE0\x82\x01QR\x7F\x0E\xAB\xA9\xF4)\xC5\xF6\xFC1\x03\xD4\xCC@V\xC5\0\xFFBB]\x8Ede\xC5\xB8\xE1E!\x9F\x9C\\\xD3` a\x01\xE0\x83\x01Q\x01R\x7F)\xAE5\x1D\t\xDC\xF4\x1C\n\x80\xAB\x059785\x8B\xAA\xB3~o\xBCFK;\xB12X\x99J\x1F\xA4a\x02\0\x82\x01QR\x7F+{\xC7F\x08\xD7\xEC}\xAD\xD0Y}j@\x10\xD8\xBF\xC2\xB3\x19\0(\x19\x01\xCE\xDCB\xBD\xBB\x0F\xB8\xFC` a\x02\0\x83\x01Q\x01R\x7F\x06h\x02\xC7\xCE\xB9\xE9\x13\xD4\xF6T3\xA2\x06a\xE0\x97\xAC\xAC\x1A\xFF\xEC\xBBSJT\xF7j)x\"&a\x02 \x82\x01QR\x7F'\xEC\x80\xE8\x11\xE66\xF34\x82g\x92<\x8Ed\x1B\xD9\x8A~7\xC5!fp\xCB\xFF\x14\xAE2?\x9E\x0E` a\x02 \x83\x01Q\x01R\x7F\x12`M\x1F\x87\xC5\x83\xF6\xC9q\x0Cs\xEA\xF5\x90\xAF\x9D\x07\xAAt=\x13\x81\xD0\xE9\xDF\xF0\xEA\xB2aB9a\x02@\x82\x01QR\x7F\x15\x88W\x9El3x\xEA2\xCBd\x12\x05\xEFv*c\xCD5:\x0B\xD6p9E(\xAD \x81\xEE\x8D\xD4` a\x02@\x83\x01Q\x01R\x7F$}e&\x1D:J\xB0B\xBA\x93s1\xF6\xD0\xC0\xC5\xEB\x9E\xA7\x87S\xA9 \x84\xDB\x1Ai9\xE1\x9E\x82a\x02`\x82\x01QR\x7F,\xE6\xCCfJ2\x14{\xFEj\x0C\x94\xA9[\xF0Ify@\\\xCA\xE0\x16H\xCDN\xC0!\x14Q \xD5` a\x02`\x83\x01Q\x01R\x90V[`\0a\x1F\xBD\x82a!bV[a\x1F\xE0\x83`\0\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[` \x02` \x01\x01Qa\x0F\xD9V[a\x1F\xF6\x83`\x01\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a \x0C\x83`\x02\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a \"\x83`\x03\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a 8\x83`\x04\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a N\x83`\x05\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a d\x83`\x06\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a z\x83`\x07\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[`\0a \x87\x85\x85\x85a\"\x9AV[\x90Pa \x92\x81a$\x1BV[\x91PP[\x93\x92PPPV[a \xA6\x82a(\xE3V[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a \xEBWa\x15\xD7\x82\x82a)\x8BV[a\x0B\x92a*\x03V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16a\x0CuW`@Q\x7F\xD7\xE6\xBC\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x0F\x16a \xF3V[\x80Qa!m\x90a*;V[a!z\x81` \x01Qa*;V[a!\x87\x81`@\x01Qa*;V[a!\x94\x81``\x01Qa*;V[a!\xA1\x81`\x80\x01Qa*;V[a!\xAE\x81`\xA0\x01Qa*;V[a!\xBB\x81`\xC0\x01Qa*;V[a!\xC8\x81`\xE0\x01Qa*;V[a!\xD6\x81a\x01\0\x01Qa*;V[a!\xE4\x81a\x01 \x01Qa*;V[a!\xF2\x81a\x01@\x01Qa*;V[a\"\0\x81a\x01`\x01Qa*;V[a\"\x0E\x81a\x01\x80\x01Qa*;V[a\"\x1C\x81a\x01\xA0\x01Qa\x0F\xD9V[a\"*\x81a\x01\xC0\x01Qa\x0F\xD9V[a\"8\x81a\x01\xE0\x01Qa\x0F\xD9V[a\"F\x81a\x02\0\x01Qa\x0F\xD9V[a\"T\x81a\x02 \x01Qa\x0F\xD9V[a\"b\x81a\x02@\x01Qa\x0F\xD9V[a\"p\x81a\x02`\x01Qa\x0F\xD9V[a\"~\x81a\x02\x80\x01Qa\x0F\xD9V[a\"\x8C\x81a\x02\xA0\x01Qa\x0F\xD9V[a\x0Fb\x81a\x02\xC0\x01Qa\x0F\xD9V[a\"\xA2aS\xE1V[\x83` \x01Q\x83Q\x14a\"\xE0W`@Q\x7FA\xF5;\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\"\xED\x85\x85\x85a*\xE5V[\x90P`\0a\"\xFE\x86`\0\x01Qa.\x16V[\x90P`\0a#\x11\x82\x84`\xA0\x01Q\x88a1\xFAV[`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x03\xC0\x806\x837PP`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a#TW\x90PP\x90P`\0a#\x8D\x8A\x85\x8A\x89\x87\x87a2ZV[`\xA0\x87\x01Q``\x87\x01Q\x91\x92P\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01`\0\x81\x83\x85\t`@\x80Qa\x01\0\x81\x01\x82R`\xE0\x9C\x8D\x01Q\x81R` \x81\x01\x96\x90\x96R\x85\x01RPPP``\x81\x01\x91\x90\x91R`\x80\x81\x01\x92\x90\x92R`\xA0\x82\x01Ra\x01`\x86\x01Q`\xC0\x82\x01Ra\x01\x80\x90\x95\x01Q\x92\x85\x01\x92\x90\x92RP\x91\x94\x93PPPPV[`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x80\x83\x01\x82\x90R\x83Q\x80\x85\x01\x85R\x82\x81R\x90\x81\x01\x82\x90R\x83Q`\x02\x80\x82R``\x82\x01\x90\x95R\x91\x93\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x93\x92\x85\x91\x81` \x01` \x82\x02\x806\x837PP`@\x80Q`\x02\x80\x82R``\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a$\xA1W\x90PP\x90P`\0`\x01\x90P\x80\x83`\0\x81Q\x81\x10a$\xE4Wa$\xE4aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x87`\xC0\x01Q\x82`\0\x81Q\x81\x10a%\x08Wa%\x08aY\xDBV[` \x02` \x01\x01\x81\x90RP\x87`\0\x01Q\x83`\x01\x81Q\x81\x10a%+Wa%+aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x87`\xE0\x01Q\x82`\x01\x81Q\x81\x10a%OWa%OaY\xDBV[` \x02` \x01\x01\x81\x90RPa%d\x82\x84a2\x8FV[`\x80\x89\x01QQ\x90\x95P``\x93P\x83\x92P\x90P`\0a%\x83\x82`\x02aZ\nV[a%\x8E\x90`\x01aZ\nV[\x90P\x80g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a%\xA9Wa%\xA9aT\xA2V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a%\xD2W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x80g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a%\xEEWa%\xEEaT\xA2V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a&3W\x81` \x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a&\x0CW\x90P[P\x92PPP`\0\x80`\0[\x89`\x80\x01QQ\x81\x10\x15a&\xD7W\x89`\x80\x01Q\x81\x81Q\x81\x10a&aWa&aaY\xDBV[` \x02` \x01\x01Q\x85\x83\x81Q\x81\x10a&{Wa&{aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x89`\xA0\x01Q\x81\x81Q\x81\x10a&\x9DWa&\x9DaY\xDBV[` \x02` \x01\x01Q\x84\x83\x81Q\x81\x10a&\xB7Wa&\xB7aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x01Ra&\xCD`\x01\x83aZ\nV[\x91P`\x01\x01a&>V[P\x88` \x01Q\x84\x82\x81Q\x81\x10a&\xEFWa&\xEFaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88`\xC0\x01Q\x83\x82\x81Q\x81\x10a'\x12Wa'\x12aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x01Ra'(`\x01\x82aZ\nV[\x89Q`@\x8B\x01Q\x91\x92P\x90`\0\x89\x82\x84\t\x90P\x80\x87\x85\x81Q\x81\x10a'NWa'NaY\xDBV[` \x02` \x01\x01\x81\x81RPPPPP\x88`\xE0\x01Q\x83\x82\x81Q\x81\x10a'tWa'taY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x01Ra'\x8A`\x01\x82aZ\nV[``\x8A\x01Q\x90\x91P\x87\x81\x84\x08\x92PPa'\xA2\x82a3\x89V[\x84\x82\x81Q\x81\x10a'\xB4Wa'\xB4aY\xDBV[` \x02` \x01\x01\x81\x81RPPa'\xEC`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x91\x82\x01R\x81Q\x80\x83\x01\x90\x92R`\x01\x82R`\x02\x90\x82\x01R\x90V[\x83\x82\x81Q\x81\x10a'\xFEWa'\xFEaY\xDBV[` \x02` \x01\x01\x81\x90RPa(\x1Ba(\x16\x84\x86a2\x8FV[a3\xDFV[\x94PPPPP`\0`@Q\x80`\x80\x01`@R\x80\x7F\x01\x18\xC4\xD5\xB87\xBC\xC2\xBC\x89\xB5\xB3\x98\xB5\x97N\x9FYD\x07;2\x07\x8B~#\x1F\xEC\x93\x88\x83\xB0\x81R` \x01\x7F&\x0E\x01\xB2Q\xF6\xF1\xC7\xE7\xFFNX\x07\x91\xDE\xE8\xEAQ\xD8z5\x8E\x03\x8BN\xFE0\xFA\xC0\x93\x83\xC1\x81R` \x01\x7F\"\xFE\xBD\xA3\xC0\xC0c*VG[B\x14\xE5a^\x11\xE6\xDD?\x96\xE6\xCE\xA2\x85J\x87\xD4\xDA\xCC^U\x81R` \x01\x7F\x04\xFCci\xF7\x11\x0F\xE3\xD2QV\xC1\xBB\x9Ar\x85\x9C\xF2\xA0FA\xF9\x9B\xA4\xEEA<\x80\xDAj_\xE4\x81RP\x90Pa(\xD9\x83\x82\x84a(\xD4a4~V[a5OV[\x96\x95PPPPPPV[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a)2W`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16`\x04\x82\x01R`$\x01a\tBV[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa)\xA8\x91\x90aZ\x1DV[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a)\xE3W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a)\xE8V[``\x91P[P\x91P\x91Pa)\xF8\x85\x83\x83a63V[\x92PPP[\x92\x91PPV[4\x15a\x0CuW`@Q\x7F\xB3\x98\x97\x9F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80Q` \x82\x01Q`\0\x91\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\x91\x15\x90\x15\x16\x15a*uWPPPV[\x82Q` \x84\x01Q\x82`\x03\x84\x85\x85\x86\t\x85\t\x08\x83\x82\x83\t\x14\x83\x82\x10\x84\x84\x10\x16\x16\x93PPP\x81a\x15\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x17`$\x82\x01R\x7FBn254: invalid G1 point\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[a+-`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@\x80Q\x80\x82\x01\x90\x91R``\x81R`\0` \x82\x01R\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01a+n\x82\x87\x87a6\xA8V[\x81Q\x84Qa+{\x90a:\x89V[a+\x88\x86` \x01Qa:\x89V[a+\x95\x87`@\x01Qa:\x89V[a+\xA2\x88``\x01Qa:\x89V[a+\xAF\x89`\x80\x01Qa:\x89V[`@Q` \x01a+\xC4\x96\x95\x94\x93\x92\x91\x90aZ9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra+\xDF\x82a;LV[Pa+\xE9\x82a;LV[``\x84\x01Ra+\xF7\x82a;LV[`\x80\x84\x01R\x81Q`\xA0\x85\x01Qa,\x0C\x90a:\x89V[`@Q` \x01a,\x1D\x92\x91\x90aZ\xB8V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,8\x82a;LV[\x83R\x81Q`\xC0\x85\x01Qa,J\x90a:\x89V[a,W\x86`\xE0\x01Qa:\x89V[a,e\x87a\x01\0\x01Qa:\x89V[a,s\x88a\x01 \x01Qa:\x89V[a,\x81\x89a\x01@\x01Qa:\x89V[`@Q` \x01a,\x96\x96\x95\x94\x93\x92\x91\x90aZ9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\xB1\x82a;LV[`\xA0\x84\x01R\x81Qa\x01\xA0\x85\x01Qa,\xC7\x90a;\xC0V[a,\xD5\x86a\x01\xC0\x01Qa;\xC0V[a,\xE3\x87a\x01\xE0\x01Qa;\xC0V[a,\xF1\x88a\x02\0\x01Qa;\xC0V[a,\xFF\x89a\x02 \x01Qa;\xC0V[`@Q` \x01a-\x14\x96\x95\x94\x93\x92\x91\x90aZ\xE7V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x83Ra\x02@\x85\x01Qa-6\x90a;\xC0V[a-D\x86a\x02`\x01Qa;\xC0V[a-R\x87a\x02\x80\x01Qa;\xC0V[a-`\x88a\x02\xA0\x01Qa;\xC0V[a-n\x89a\x02\xC0\x01Qa;\xC0V[`@Q` \x01a-\x83\x96\x95\x94\x93\x92\x91\x90aZ\xE7V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra-\x9E\x82a;LV[`\xC0\x84\x01R\x81Qa\x01`\x85\x01Qa-\xB4\x90a:\x89V[a-\xC2\x86a\x01\x80\x01Qa:\x89V[`@Q` \x01a-\xD4\x93\x92\x91\x90a[#V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra-\xEF\x82a;LV[`\xE0\x84\x01R\x82Q\x81\x81\x80\t\x82\x82\x82\t` \x86\x01\x91\x90\x91R`@\x85\x01RP\x91\x95\x94PPPPPV[a.H`@Q\x80`\xA0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[\x81b\x01\0\0\x03a.\xDCWP`@\x80Q`\xA0\x81\x01\x82R`\x10\x81R` \x81\x01\x92\x90\x92R\x7F0d\x1E\x0E\x92\xBE\xBE\xF8\x18&\x8Df;\xCA\xD6\xDB\xCF\xD6\xC0\x14\x91p\xF6\xD7\xD3P\xB1\xB1\xFAl\x10\x01\x90\x82\x01R~\xEE\xB2\xCBY\x81\xEDEd\x9A\xBE\xBD\xE0\x81\xDC\xFF\x16\xC8`\x1D\xE44~}\xD1b\x8B\xA2\xDA\xACC\xB7``\x82\x01R\x7F\x0B]V\xB7\x7F\xE7\x04\xE8\xE9#8\xC0\x08/7\xE0\x91\x12d\x14\xC80\xE4\xC6\x92-Z\xC8\x02\xD8B\xD4`\x80\x82\x01R\x90V[\x81b\x02\0\0\x03a/qWP`@\x80Q`\xA0\x81\x01\x82R`\x11\x81R` \x81\x01\x92\x90\x92R\x7F0d6@\xB9\xF8/\x90\xE8;i\x8E^\xA6\x17\x9C|\x05T.\x85\x953\xB4\x8B\x99S\xA2\xF56\x08\x01\x90\x82\x01R\x7F\x1B\xF8-\xEB\xA7\xD7I\x02\xC3p\x8C\xC6\xE7\x0Ea\xF3\x05\x12\xEC\xA9VU!\x0E'nXX\xCE\x8FX\xE5``\x82\x01R\x7F$L\xF0\x10\xC4<\xA8r7\xD8\xB0\x0B\xF9\xDDP\xC4\xC0\x1C\x7F\x08k\xD4\xE8\xC9 \xE7RQ\xD9o\r\"`\x80\x82\x01R\x90V[\x81b\x04\0\0\x03a0\x06WP`@\x80Q`\xA0\x81\x01\x82R`\x12\x81R` \x81\x01\x92\x90\x92R\x7F0dBY\xCD\x94\xE7\xDDPE\xD7\xA2p\x13\xB7\xFC\xD2\x1C\x9E;\x7F\xA7R\"\xE7\xBD\xA4\x9Br\x9B\x04\x01\x90\x82\x01R\x7F\x19\xDD\xBC\xAF:\x8DF\xC1\\\x01v\xFB\xB5\xB9^M\xC5p\x88\xFF\x13\xF4\xD1\xBD\x84\xC6\xBF\xA5}\xCD\xC0\xE0``\x82\x01R\x7F\x03hS\xF0\x83x\x0E\x87\xF8\xD7\xC7\x1D\x11\x11\x19\xC5}\xBE\x11\x8C\"\xD5\xADpz\x821tf\xC5\x17L`\x80\x82\x01R\x90V[\x81b\x08\0\0\x03a0\x9BWP`@\x80Q`\xA0\x81\x01\x82R`\x13\x81R` \x81\x01\x92\x90\x92R\x7F0dHfWcD\x03\x84K\x0E\xACx\xCA\x88,\xFD(CA\xFC\xB0aZ\x15\xCF\xCD\x17\xB1M\x82\x01\x90\x82\x01R\x7F\"`\xE7$\x84K\xCARQ\x82\x93S\x96\x8EI\x150RXA\x83WG:\\\x1DY\x7Fa?l\xBD``\x82\x01R\x7F\x06\xE4\x02\xC0\xA3\x14\xFBg\xA1\\\xF8\x06fJ\xE1\xB7\"\xDB\xC0\xEF\xE6nl\x81\xD9\x8F\x99$\xCASS!`\x80\x82\x01R\x90V[\x81b\x10\0\0\x03a10WP`@\x80Q`\xA0\x81\x01\x82R`\x14\x81R` \x81\x01\x92\x90\x92R\x7F0dKl\x9CJr\x16\x9EM\xAA1}%\xF0E\x12\xAE\x15\xC5;4\xE8\xF5\xAC\xD8\xE1U\xD0\xA6\xC1\x01\x90\x82\x01R\x7F&\x12]\xA1\n\x0E\xD0c'P\x8A\xBA\x06\xD1\xE3\x03\xACaf2\xDB\xED4\x9FSB-\xA9S3xW``\x82\x01R\x7F\x10\x0C3-!\0\x89_\xABds\xBC,Q\xBF\xCAR\x1FE\xCB;\xAC\xA6&\x08R\xA8\xFD\xE2l\x91\xF3`\x80\x82\x01R\x90V[\x81` \x03a1\xC3WP`@\x80Q`\xA0\x81\x01\x82R`\x05\x81R` \x81\x01\x92\x90\x92R\x7F.\xE1+\xFFJ(\x13(j\x8D\xC3\x88\xCDuM\x9A>\xF2I\x065\xEB\xA5\x0C\xB9\xC2\xE5\xE7P\x80\0\x01\x90\x82\x01R\x7F\t\xC52\xC60k\x93\xD2\x96x \rG\xC0\xB2\xA9\x9C\x18\xD5\x1B\x83\x8E\xEB\x1D>\xEDLS;\xB5\x12\xD0``\x82\x01R\x7F'$q6\x03\xBF\xBDy\n\xEA\xF3\xE7\xDF%\xD8\xE7\xEF\x8F1\x134\x90[M\x8C\x99\x98\x0C\xF2\x10\x97\x9D`\x80\x82\x01R\x90V[`@Q\x7F\xE2\xEF\t\xE5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x91\x90PV[a2\x1E`@Q\x80``\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[a2(\x84\x84a=\x0CV[\x80\x82Ra28\x90\x85\x90\x85\x90a=rV[` \x82\x01R\x80Qa2N\x90\x85\x90\x84\x90\x86\x90a=\xF8V[`@\x82\x01R\x93\x92PPPV[`\0\x80a2h\x85\x87\x89a?\xBFV[\x90Pa2x\x88\x86\x89\x89\x88\x88a@\xBDV[a2\x83\x81\x87\x86aC\xDBV[\x98\x97PPPPPPPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x82Q\x82Q\x14a2\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FMSM error: length does not match`D\x82\x01R`d\x01a\tBV[a32\x83`\0\x81Q\x81\x10a3\nWa3\naY\xDBV[` \x02` \x01\x01Q\x83`\0\x81Q\x81\x10a3%Wa3%aY\xDBV[` \x02` \x01\x01QaD=V[\x90P`\x01[\x82Q\x81\x10\x15a3\x82Wa3x\x82a3s\x86\x84\x81Q\x81\x10a3YWa3YaY\xDBV[` \x02` \x01\x01Q\x86\x85\x81Q\x81\x10a3%Wa3%aY\xDBV[aD\xE1V[\x91P`\x01\x01a37V[P\x92\x91PPV[`\0a3\xB5\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x83a[|V[a)\xFD\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01a[\x9EV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81Q` \x83\x01Q\x15\x90\x15\x16\x15a4\x07WP\x90V[`@Q\x80`@\x01`@R\x80\x83`\0\x01Q\x81R` \x01\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\x84` \x01Qa4L\x91\x90a[|V[a4v\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDGa[\x9EV[\x90R\x92\x91PPV[a4\xA9`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80`\x80\x01`@R\x80\x7F\x18\0\xDE\xEF\x12\x1F\x1EvBj\0f^\\DygC\"\xD4\xF7^\xDA\xDDF\xDE\xBD\\\xD9\x92\xF6\xED\x81R` \x01\x7F\x19\x8E\x93\x93\x92\rH:r`\xBF\xB71\xFB]%\xF1\xAAI35\xA9\xE7\x12\x97\xE4\x85\xB7\xAE\xF3\x12\xC2\x81R` \x01\x7F\x12\xC8^\xA5\xDB\x8Cm\xEBJ\xABq\x80\x8D\xCB@\x8F\xE3\xD1\xE7i\x0CC\xD3{L\xE6\xCC\x01f\xFA}\xAA\x81R` \x01\x7F\t\x06\x89\xD0X_\xF0u\xEC\x9E\x99\xADi\x0C3\x95\xBCK13p\xB3\x8E\xF3U\xAC\xDA\xDC\xD1\"\x97[\x81RP\x90P\x90V[`\0\x80`\0`@Q\x87Q\x81R` \x88\x01Q` \x82\x01R` \x87\x01Q`@\x82\x01R\x86Q``\x82\x01R``\x87\x01Q`\x80\x82\x01R`@\x87\x01Q`\xA0\x82\x01R\x85Q`\xC0\x82\x01R` \x86\x01Q`\xE0\x82\x01R` \x85\x01Qa\x01\0\x82\x01R\x84Qa\x01 \x82\x01R``\x85\x01Qa\x01@\x82\x01R`@\x85\x01Qa\x01`\x82\x01R` `\0a\x01\x80\x83`\x08Z\xFA\x91PP`\0Q\x91P\x80a6%W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1C`$\x82\x01R\x7FBn254: Pairing check failed!\0\0\0\0`D\x82\x01R`d\x01a\tBV[P\x15\x15\x90P[\x94\x93PPPPV[``\x82a6HWa6C\x82aE\x88V[a \x96V[\x81Q\x15\x80\x15a6_WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a6\xA1W`@Q\x7F\x99\x96\xB3\x15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x85\x16`\x04\x82\x01R`$\x01a\tBV[P\x80a \x96V[\x82Q`\xFE\x90a6\xE3a6\xB9\x83a;\xC0V[`@Q` \x01a6\xCB\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x04aE\xCAV[a7\x1Da6\xF3\x86`\0\x01Qa;\xC0V[`@Q` \x01a7\x05\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x08aE\xCAV[a7-a6\xF3\x87` \x01Qa;\xC0V[`@Q` \x01a7@\x94\x93\x92\x91\x90a[\xB1V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85Ra7]`\x01a;\xC0V[a7\x86\x7F/\x8D\xD1\xF1\xA7X\x8BW`\x01`\0[\x82\x81\x10\x15a>~W\x81\x87\x03a>_W\x87\x81\x81Q\x81\x10a>LWa>LaY\xDBV[` \x02` \x01\x01Q\x94PPPPPa6+V[\x83\x80a>mWa>ma[fV[\x89``\x01Q\x83\t\x91P`\x01\x01a>+V[P`\0\x93PPPPa6+V[`\0\x80`\0\x80\x8A`@\x01Q\x90P`\0\x80a>\xA5\x8D\x88aG\xAAV[\x90P`\0\x87g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a>\xC2Wa>\xC2aT\xA2V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a>\xEBW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P\x88\x8B\x85\t\x93P`\x01\x92P`\0[\x88\x81\x10\x15a?0W` \x81\x02` \x84\x01\x01Q\x95P\x89\x8D\x87\x8C\x03\x08\x96P\x89\x87\x85\t` \x82\x81\x02\x84\x01\x01\x88\x90R\x93P`\x01\x01a>\xFBV[Pa?:\x83aF\xF2V[\x92P`\0[\x88\x81\x10\x15a?\xADW` \x81\x02` \x84\x01\x01Q\x95P\x89\x86\x86\t\x97P\x89\x84\x89\t\x97P`\0[\x89\x81\x10\x15a?\x8CW\x80\x82\x14a?\x84W` \x81\x02` \x84\x01\x01Q\x97P\x8A\x88\x8A\t\x98P[`\x01\x01a?bV[P` \x81\x02` \x8F\x01\x01Q\x95P\x89\x86\x89\t\x97P\x89\x88\x8C\x08\x9AP`\x01\x01a??V[PPPPPPPPPP\x94\x93PPPPV[`\0\x80\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P`\0\x83` \x01Q\x90P`\0\x84`@\x01Q\x90P`\0`\x01\x90P``\x88\x01Q`\x80\x89\x01Qa\x01\xA0\x89\x01Qa\x02@\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xC0\x89\x01Qa\x02`\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xE0\x89\x01Qa\x02\x80\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02\0\x89\x01Qa\x02\xA0\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02 \x89\x01Q\x91Pa\x02\xC0\x89\x01Q\x86\x87\x82\x89\x85\x87\x08\t\x85\t\x93PPPP\x87Q` \x89\x01Q\x85\x86\x86\x83\t\x87\x03\x85\x08\x96PP\x84\x85\x83\x83\t\x86\x03\x87\x08\x99\x98PPPPPPPPPV[a@\xCB\x86\x86\x86\x86\x85\x87aH\x9BV[`\xC0\x85\x01Q\x82Q\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x91\x90\x81\x90\x81\x90\x86\x90`\x14\x90\x81\x10aA\x0CWaA\x0CaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85`\0\x01Q\x84`\x14\x81Q\x81\x10aA0WaA0aY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x15\x81Q\x81\x10aAUWaAUaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85` \x01Q\x84`\x15\x81Q\x81\x10aAyWaAyaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x16\x81Q\x81\x10aA\x9EWaA\x9EaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85`@\x01Q\x84`\x16\x81Q\x81\x10aA\xC2WaA\xC2aY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x17\x81Q\x81\x10aA\xE7WaA\xE7aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85``\x01Q\x84`\x17\x81Q\x81\x10aB\x0BWaB\x0BaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x18\x81Q\x81\x10aB0WaB0aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85`\x80\x01Q\x84`\x18\x81Q\x81\x10aBTWaBTaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x19\x81Q\x81\x10aByWaByaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88`@\x01Q\x84`\x19\x81Q\x81\x10aB\x9DWaB\x9DaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1A\x81Q\x81\x10aB\xC2WaB\xC2aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88``\x01Q\x84`\x1A\x81Q\x81\x10aB\xE6WaB\xE6aY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1B\x81Q\x81\x10aC\x0BWaC\x0BaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88`\x80\x01Q\x84`\x1B\x81Q\x81\x10aC/WaC/aY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1C\x81Q\x81\x10aCTWaCTaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88`\xA0\x01Q\x84`\x1C\x81Q\x81\x10aCxWaCxaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x87`\xE0\x01Q\x85`\x1D\x81Q\x81\x10aC\xA1WaC\xA1aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85`\xA0\x01Q\x84`\x1D\x81Q\x81\x10aC\xC5WaC\xC5aY\xDBV[` \x02` \x01\x01\x81\x90RPPPPPPPPPPV[\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x83\x81\x03\x90`\0[`\n\x81\x10\x15aD4W` `\x15\x82\x01\x02\x84\x01Q` \x82\x02a\x01\xA0\x01\x86\x01Q\x83\x84\x82\x84\t\x86\x08\x94PPP`\x01\x01aD\x03V[PP\x93\x92PPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RaDYaT4V[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x81\x01\x83\x90R`\0``\x83`\x80\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80aD\x8BW`\0\x80\xFD[P\x80aD\xD9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01R\x7FBn254: scalar mul failed!\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[PP\x92\x91PPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RaD\xFDaTRV[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x83\x01R\x83\x01Q``\x80\x83\x01\x91\x90\x91R`\0\x90\x83`\xC0\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80aE:W`\0\x80\xFD[P\x80aD\xD9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: group addition failed!\0\0\0`D\x82\x01R`d\x01a\tBV[\x80Q\x15aE\x98W\x80Q\x80\x82` \x01\xFD[`@Q\x7F\x14%\xEAB\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x81aE\xD8\x81`\x1FaZ\nV[\x10\x15aF&W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x0E`$\x82\x01R\x7Fslice_overflow\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[aF0\x82\x84aZ\nV[\x84Q\x10\x15aF\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x11`$\x82\x01R\x7Fslice_outOfBounds\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[``\x82\x15\x80\x15aF\x9FW`@Q\x91P`\0\x82R` \x82\x01`@RaF\xE9V[`@Q\x91P`\x1F\x84\x16\x80\x15` \x02\x81\x84\x01\x01\x85\x81\x01\x87\x83\x15` \x02\x84\x8B\x01\x01\x01[\x81\x83\x10\x15aF\xD8W\x80Q\x83R` \x92\x83\x01\x92\x01aF\xC0V[PP\x85\x84R`\x1F\x01`\x1F\x19\x16`@RP[P\x94\x93PPPPV[`\0\x80`\0\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P`@Q` \x81R` \x80\x82\x01R` `@\x82\x01R\x84``\x82\x01R`\x02\x82\x03`\x80\x82\x01R\x81`\xA0\x82\x01R` `\0`\xC0\x83`\x05Z\xFA\x92PP`\0Q\x92P\x81aG\xA3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: pow precompile failed!\0\0\0`D\x82\x01R`d\x01a\tBV[PP\x91\x90PV[``\x82` \x01Q\x82\x11\x15aG\xEAW`@Q\x7F\x8C^\x11\xF1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x83\x01Q`\x01\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x84g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15aH+WaH+aT\xA2V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15aHTW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x84\x15\x19\x15a=iW` \x84\x01\x85` \x02\x81\x01`\x01\x82R` \x82\x01\x91P[\x80\x82\x10\x15aH\x90W\x82\x85\x85\t\x93P\x83\x82R` \x82\x01\x91PaHtV[PPPPP\x92\x91PPV[`\0\x80`\0\x80`\0\x80\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P\x80` \x8B\x01Q` \x8D\x01Q\t\x95P\x8AQ\x93P\x80`\xA0\x8C\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80\x7F/\x8D\xD1\xF1\xA7XW`\0\x80\xFD[\x815\x81\x81\x11\x15aXPWaXPaT\xA2V[aXb\x84`\x1F\x19`\x1F\x84\x01\x16\x01aT\xE2V[\x91P\x80\x82R\x87\x84\x82\x85\x01\x01\x11\x15aXxW`\0\x80\xFD[\x80\x84\x84\x01\x85\x84\x017`\0\x84\x82\x84\x01\x01RP\x80\x93PPPP\x92P\x92\x90PV[\x805c\xFF\xFF\xFF\xFF\x81\x16\x81\x14a1\xF5W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aX\xBCW`\0\x80\xFD[a \x96\x82aX\x96V[`\0\x80`\0a\x01@\x84\x86\x03\x12\x15aX\xDBW`\0\x80\xFD[aX\xE5\x85\x85aU+V[\x92PaX\xF4a\x01\0\x85\x01aX\x96V[\x91PaY\x03a\x01 \x85\x01aTpV[\x90P\x92P\x92P\x92V[`\0a\x01\0\x82\x84\x03\x12\x15aY\x1FW`\0\x80\xFD[a \x96\x83\x83aU+V[`\0[\x83\x81\x10\x15aYDW\x81\x81\x01Q\x83\x82\x01R` \x01aY,V[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01RaYl\x81`@\x85\x01` \x87\x01aY)V[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x81\x16\x83\x82\x16\x02\x80\x82\x16\x91\x90\x82\x81\x14aD\xD9WaD\xD9aY\x80V[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x81\x16\x83\x82\x16\x01\x90\x80\x82\x11\x15a3\x82Wa3\x82aY\x80V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0` \x82\x84\x03\x12\x15aZ\x03W`\0\x80\xFD[PQ\x91\x90PV[\x80\x82\x01\x80\x82\x11\x15a)\xFDWa)\xFDaY\x80V[`\0\x82QaZ/\x81\x84` \x87\x01aY)V[\x91\x90\x91\x01\x92\x91PPV[`\0\x87Q` aZL\x82\x85\x83\x8D\x01aY)V[\x88Q\x91\x84\x01\x91aZ_\x81\x84\x84\x8D\x01aY)V[\x88Q\x92\x01\x91aZq\x81\x84\x84\x8C\x01aY)V[\x87Q\x92\x01\x91aZ\x83\x81\x84\x84\x8B\x01aY)V[\x86Q\x92\x01\x91aZ\x95\x81\x84\x84\x8A\x01aY)V[\x85Q\x92\x01\x91aZ\xA7\x81\x84\x84\x89\x01aY)V[\x91\x90\x91\x01\x99\x98PPPPPPPPPV[`\0\x83QaZ\xCA\x81\x84` \x88\x01aY)V[\x83Q\x90\x83\x01\x90aZ\xDE\x81\x83` \x88\x01aY)V[\x01\x94\x93PPPPV[`\0\x87QaZ\xF9\x81\x84` \x8C\x01aY)V[\x91\x90\x91\x01\x95\x86RP` \x85\x01\x93\x90\x93R`@\x84\x01\x91\x90\x91R``\x83\x01R`\x80\x82\x01R`\xA0\x01\x91\x90PV[`\0\x84Qa[5\x81\x84` \x89\x01aY)V[\x84Q\x90\x83\x01\x90a[I\x81\x83` \x89\x01aY)V[\x84Q\x91\x01\x90a[\\\x81\x83` \x88\x01aY)V[\x01\x95\x94PPPPPV[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a[\x99WcNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[P\x06\x90V[\x81\x81\x03\x81\x81\x11\x15a)\xFDWa)\xFDaY\x80V[`\0\x85Qa[\xC3\x81\x84` \x8A\x01aY)V[\x85Q\x90\x83\x01\x90a[\xD7\x81\x83` \x8A\x01aY)V[\x85Q\x91\x01\x90a[\xEA\x81\x83` \x89\x01aY)V[\x84Q\x91\x01\x90a[\xFD\x81\x83` \x88\x01aY)V[\x01\x96\x95PPPPPPV[`\0\x89Q` a\\\x1B\x82\x85\x83\x8F\x01aY)V[\x8AQ\x91\x84\x01\x91a\\.\x81\x84\x84\x8F\x01aY)V[\x8AQ\x92\x01\x91a\\@\x81\x84\x84\x8E\x01aY)V[\x89Q\x92\x01\x91a\\R\x81\x84\x84\x8D\x01aY)V[\x88Q\x92\x01\x91a\\d\x81\x84\x84\x8C\x01aY)V[\x87Q\x92\x01\x91a\\v\x81\x84\x84\x8B\x01aY)V[\x86Q\x92\x01\x91a\\\x88\x81\x84\x84\x8A\x01aY)V[\x85Q\x92\x01\x91a\\\x9A\x81\x84\x84\x89\x01aY)V[\x91\x90\x91\x01\x9B\x9APPPPPPPPPPPV[`\0\x88Q` a\\\xC0\x82\x85\x83\x8E\x01aY)V[\x89Q\x91\x84\x01\x91a\\\xD3\x81\x84\x84\x8E\x01aY)V[\x89Q\x92\x01\x91a\\\xE5\x81\x84\x84\x8D\x01aY)V[\x88Q\x92\x01\x91a\\\xF7\x81\x84\x84\x8C\x01aY)V[\x87Q\x92\x01\x91a]\t\x81\x84\x84\x8B\x01aY)V[\x86Q\x92\x01\x91a]\x1B\x81\x84\x84\x8A\x01aY)V[\x85Q\x92\x01\x91a]-\x81\x84\x84\x89\x01aY)V[\x91\x90\x91\x01\x9A\x99PPPPPPPPPPV[`\0\x8AQa]Q\x81\x84` \x8F\x01aY)V[\x91\x90\x91\x01\x98\x89RP` \x88\x01\x96\x90\x96R`@\x87\x01\x94\x90\x94R``\x86\x01\x92\x90\x92R`\x80\x85\x01R`\xA0\x84\x01R`\xC0\x83\x01R`\xE0\x82\x01Ra\x01\0\x01\x91\x90PV\xFE\xA1dsolcC\0\x08\x17\0\n"; + const __BYTECODE: &[u8] = b"`\xA0`@R0`\x80R4\x80\x15b\0\0\x15W`\0\x80\xFD[Pb\0\0 b\0\0&V[b\0\0\xDAV[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15b\0\0wW`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80T`\x01`\x01`@\x1B\x03\x90\x81\x16\x14b\0\0\xD7W\x80T`\x01`\x01`@\x1B\x03\x19\x16`\x01`\x01`@\x1B\x03\x90\x81\x17\x82U`@Q\x90\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PV[`\x80QaZEb\0\x01\x04`\09`\0\x81\x81a\x142\x01R\x81\x81a\x14[\x01Ra\x15\xC7\x01RaZE`\0\xF3\xFE`\x80`@R`\x046\x10a\x01RW`\x005`\xE0\x1C\x80c\x01?\xA5\xFC\x14a\x01WW\x80c\r\x8En,\x14a\x01yW\x80c1=\xF7\xB1\x14a\x01\xABW\x80c8+!Z\x14a\x01\xD8W\x80c@\x999\xB7\x14a\x01\xFCW\x80cHG\xAE]\x14a\x02\x1CW\x80cO\x1E\xF2\x86\x14a\x02\x9EW\x80cR\xD1\x90-\x14a\x02\xB1W\x80cTd`\x85\x14a\x02\xC6W\x80cb\x82w3\x14a\x02\xDBW\x80ci\xCCj\x04\x14a\x02\xF1W\x80cpS\xFCQ\x14a\x03\x06W\x80cqP\x18\xA6\x14a\x03\x1BW\x80cvg\x18\x08\x14a\x030W\x80cv\xB6\xB7\xCB\x14a\x03dW\x80c\x7F\x17\xBA\xAD\x14a\x03zW\x80c\x82\xD0\x7F\xF3\x14a\x04-W\x80c\x85\x84\xD2?\x14a\x04BW\x80c\x8D\xA5\xCB[\x14a\x04\x86W\x80c\xA2D\xD5\x96\x14a\x04\x9BW\x80c\xA5\x1Eo\xEA\x14a\x04\xBBW\x80c\xAA\x92'2\x14a\x04\xDBW\x80c\xAD<\xB1\xCC\x14a\x04\xFBW\x80c\xBD2Q\x9A\x14a\x059W\x80c\xCAo\xE8U\x14a\x05jW\x80c\xDB\x13\xB6\n\x14a\x05\x80W\x80c\xE003\x01\x14a\x05\xBFW\x80c\xF0h T\x14a\x05\xDFW\x80c\xF2\xFD\xE3\x8B\x14a\x06\x11W[`\0\x80\xFD[4\x80\x15a\x01cW`\0\x80\xFD[Pa\x01wa\x01r6`\x04aO\x9DV[a\x061V[\0[4\x80\x15a\x01\x85W`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\xB7W`\0\x80\xFD[P`\x06Ta\x01\xCB\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Qa\x01\xA2\x91\x90aO\xB8V[4\x80\x15a\x01\xE4W`\0\x80\xFD[Pa\x01\xEE`\x03T\x81V[`@Q\x90\x81R` \x01a\x01\xA2V[4\x80\x15a\x02\x08W`\0\x80\xFD[Pa\x01wa\x02\x176`\x04aQ=V[a\x06\xF1V[4\x80\x15a\x02(W`\0\x80\xFD[Pa\x021a\n\x1CV[`@Qa\x01\xA2\x91\x90`\0a\x01\0\x82\x01\x90P`\x01\x80`@\x1B\x03\x80\x84Q\x16\x83R\x80` \x85\x01Q\x16` \x84\x01RP`@\x83\x01Q`@\x83\x01R``\x83\x01Q``\x83\x01R`\x80\x83\x01Q`\x80\x83\x01R`\xA0\x83\x01Q`\xA0\x83\x01R`\xC0\x83\x01Q`\xC0\x83\x01R`\xE0\x83\x01Q`\xE0\x83\x01R\x92\x91PPV[a\x01wa\x02\xAC6`\x04aR\xF7V[a\n\xAEV[4\x80\x15a\x02\xBDW`\0\x80\xFD[Pa\x01\xEEa\n\xCDV[4\x80\x15a\x02\xD2W`\0\x80\xFD[P`\x08Ta\x01\xEEV[4\x80\x15a\x02\xE7W`\0\x80\xFD[Pa\x01\xEE`\x02T\x81V[4\x80\x15a\x02\xFDW`\0\x80\xFD[Pa\x01wa\n\xEAV[4\x80\x15a\x03\x12W`\0\x80\xFD[P`\x07Ta\x01\xEEV[4\x80\x15a\x03'W`\0\x80\xFD[Pa\x01wa\x0BZV[4\x80\x15a\x03F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x14\xD4a\x10^V[\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x81`@Qa\x06\xE6\x91\x90aO\xB8V[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x15]WP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x15Z\x91\x81\x01\x90aUnV[`\x01[a\x15|W\x81`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\x08[\x91\x90aO\xB8V[`\0\x80Q` aYY\x839\x81Q\x91R\x81\x14a\x15\xADW`@Qc*\x87Ri`\xE2\x1B\x81R`\x04\x81\x01\x82\x90R`$\x01a\x08[V[a\x15\xB7\x83\x83a \xB2V[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\x0BXW`@Qcp>F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\x16\x0Fa\x16aV[\x80T`\x01`\x01`\xA0\x1B\x03\x84\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x93\x94P\x91\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x90V[a\x16\xB1a!\x08V[a\x10[\x81a!-V[a\x0BXa!\x08V[\x81Q`\x01`\x01`@\x1B\x03\x16\x15\x15\x80a\x16\xE6WP` \x82\x01Q`\x01`\x01`@\x1B\x03\x16\x15\x15[\x80a\x16\xF3WP`\x80\x82\x01Q\x15[\x80a\x17\0WP`\xA0\x82\x01Q\x15[\x80a\x17\rWP`\xC0\x82\x01Q\x15[\x80a\x17\x1AWP`\xE0\x82\x01Q\x15[\x80a\x17)WPc\xFF\xFF\xFF\xFF\x81\x16\x15[\x15a\x17GW`@QcP\xDD\x03\xF7`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x81`\x05`\0\x80`\x04\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP\x81`\x05`\0\x80`\x08\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP`\0\x80`\x0Ca\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP\x80`\0\x80a\x01\0\n\x81T\x81c\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83c\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`\0a\x19-\x83a\x0E\xBDV[`\x01\x81\x81U`\xE0\x85\x01Q`\x02\x81\x81U`\x03\x93\x90\x93U`\x04U`\x07\x80T\x80\x83\x01\x82U`\0\x91\x82RC`\0\x80Q` aY\x19\x839\x81Q\x91R\x90\x91\x01U`@\x80Q\x80\x82\x01\x82R` \x80\x89\x01Q`\x01`\x01`@\x1B\x03\x90\x81\x16\x83R\x92\x90\x98\x01Q\x97\x81\x01\x97\x88R`\x08\x80T\x94\x85\x01\x81U\x90\x92R\x90Q`\0\x80Q` aY\x99\x839\x81Q\x91R\x92\x90\x93\x02\x91\x82\x01\x80T`\x01`\x01`@\x1B\x03\x19\x16\x93\x90\x91\x16\x92\x90\x92\x17\x90\x91U\x92Q`\0\x80Q` aY\xD9\x839\x81Q\x91R\x90\x93\x01\x92\x90\x92UPPV[a\x19\xEDaM\xF2V[b\x10\0\0\x81R`\x08` \x82\x01R\x7F \xC9@13\xDF\xDE\x9A\x9D8-\xF7o\xB0R5qd\x87%\xAB\xC0\xA7\xC1(0\xBBi\x0E\xC8;3`@\x82\x01QR\x7F\x03\xA0\xA9\xAC\xC3\xE3\x81Z~\xD6\xCB\x13y\xF7\xD1W\xE641dr\x93v9*i:\xCB\xD3\xEC(<` `@\x83\x01Q\x01R\x7F(f\xC1\x8A\xD1\xDF\x10\xEF\x13T,\xCEbP\xCE\x02\xCB*kr\xAE\0\xA9\x85.'\x11\x87\xE9\xE4\xE0\xDB``\x82\x01QR\x7F!\xBE#*B$jVc\xEB\xF4\x83G\x0C\xCAfo\xFE\x9DO\x0Ec\xB9)\xC5\x96\xA7e\x87\x14\xE9p` ``\x83\x01Q\x01R\x7F\x07\xD7xs\xB9\x86\0t\x11\x8Eu\x80\x8CyF\x8B\x83\xC8\xEDd\xBA\x14\xDB\\\xB5\xAF\xA8\xE54\xDE{\x99`\x80\x82\x01QR\x7F\x0B\xE0\xF4H\x83\x90\x80\x13-G\xDE\x17\xDE\0\x99\xB4\xCDt\xAE\x1Ekq\xCD\xDA\x06\xCD\xEB\xB8h\xA5\x0Cm` `\x80\x83\x01Q\x01R\x7F\x13\xBDE\xA0#I\x1E\xAD\xEAD\xCC?$\xCF\xBD\x17\x96\xEA\xDE\x9C\x0E9\xEE\x81\xD9\xF6>\xA0\xA5\x80f%`\xA0\x82\x01QR\x7F\x18\xF9\\\xDD\xA4,\xE1\x1D\x9D\x10\xA3\xB35\xAC\xC2\x14\xE3\x80|W\x8CSY@]\x81\x0C \x8D\xF6\0\x93` `\xA0\x83\x01Q\x01R\x7F\tp\xD9xv4a\xF0\x9E\x9E\xC64T\x074\x978nM(/\xED\xC2\xAC[\x96|\xB9\xFD?\xA8\xA9`\xC0\x82\x01QR\x7F(\xC2!\x7F{\xAC\xF6\xF8\xB2\xB8\xEEJ\x90\xFC\xF8\xB5\xBC\xA0B\x05\xEA\x84\xE8\xE1\xEBT\xB8]\xD4\x1B\xDE(` `\xC0\x83\x01Q\x01R\x7F\x02\xFE=\x02\x98\x8D\xB7\x188\0R\x97\n\xBAF\xA3)m\xF5\xF2\x9Bsk\xA1\xF2\xC4\xCC\xFF\xC8\xB5\x96\x93`\xE0\x82\x01QR\x7F ,>9\x0C\xEE|\\\x85%\xDA#)\xA1\x9FI6\xF6\xF7\x1C\xA9}\xDElo\xA3+8-Z\xCC\x03` `\xE0\x83\x01Q\x01R\x7F#\xAC\x10\xAEl\xA5\xCA\xCE\xE8tK\xB99\xAA\xA859\tT\xB9\x1A\xE6h\xA2\xC8\xD0\xED\xDAU\x8A\x89\xE7a\x01\0\x82\x01QR\x7F\x1C\x8C+\x85l\xDA\xDE%k\xA3#\x7F9\xAF\xD5\xE1p\xA9S \x12\xF7\xAE\xCA\xE4\x9DE\x9B)\xF6\xF6\xAD` a\x01\0\x83\x01Q\x01R\x7F\x16\xEC\x03\xD2`\xBDz\xC1\xC5\x0F\xFAcV]Rt\xB4X,\xEE\xA5/\xF4\x0B\x81\xCD\xFE\x8FDO\x01\xE4a\x01 \x82\x01QR\x7F)9!Rr0\x97\xE0q\x13\xC3\xD7xm$^\xC4\x0C0\x92\x80\x15\xCDP\xB5f\x8AON\xA1p1` a\x01 \x83\x01Q\x01R\x7F,\xDB\xFB:@S\xC8H\x9B\x0C\x94\xE7C8\xAC\x19\x11\x8D\xF7\xA0k\xC5k\x1E\xB4\xD0\xE0\xDCN\xAErHa\x01@\x82\x01QR\x7F\x07\xFE\xA1'\xDA\xE9C\xB8\xDC\x14\x8F\x14\x08\xD4\x0C\xFFF\\\x9CG!\x946i\xB1\xE4\xFDZ9\xDBp6` a\x01@\x83\x01Q\x01R\x7F\x03\x14U\xA7\x9A.\x0C\xE7\x8Al\xB55&\xEC\x04\xAC\x19qj\x86\xB0\x8A\x93\xDFH\xD1x\xF8\xB7~V\x19a\x01`\x82\x01QR\x7F\x11\x86#\xE6\xBC\x13n\xE6\xD3\xF9\x90|\xD4\xAD\x04\xA9A\x8E\xA0;\xA9\x9A\xD7S\"|\xDF\xEEY\x8E\x84\x15` a\x01`\x83\x01Q\x01R\x7F\x08a\xD1\x99wa\xA8R\"j\xAC{\xA9q{\xF6\xAEVE\x10\x99\xBEwL\xDF\x02\xEF5*X\xCB\xC8a\x01\x80\x82\x01QR\x7F\x08\x05\xE3\x92\xBC\xBC\x12\xE4\nr'xc-s\xFE\x98\x1EK\xC6\xFAm\x11x\xB7\n\xF7\xBE\x1C\xB9\xA3\xA3` a\x01\x80\x83\x01Q\x01R\x7F\x10\x1D\x1E9x\xCB\x9F\x1E0=A1D\xEB\xE6v\x82\xC9\xEB\x0C\xFE\x11$)Y\xAA`)\xD7\x8C\xDB\xBCa\x01\xA0\x82\x01QR\x7F\x08\x9E\xB9\xC7'\xE6\xCB\x07\x08+\xC3\xE6\xF4\x0C\xF0OC\x9F\xE4\x80\0`+XGt\xDA\xD7\xEF\xC6`|` a\x01\xA0\x83\x01Q\x01R\x7F-H\x9F$\x93&:\xA8s\xBC\xD9O!\xEF\xB4[\xF2W\xA6\x1D\x81\xC0\xC9\\2\x97\x91e\x06e;@a\x01\xC0\x82\x01QR\x7F\x18\xE4]bz\xAD\xD4\xDF'\x94\xEC\xD9\x90\x9F\xAC\x1Au?\x0Co\xA8\xA9\xC6eJzX\xB0\x91/\xFF\xD5` a\x01\xC0\x83\x01Q\x01R\x7F\x0EC\xE3\xA4\xB1<\xB48\xE2\xAD\x92F\x14&\x1A\xD0$\x02\x14\xFA\x1C\x83\xFC\xDAj\x0B\xF7y\xEB9\xFF\xC5a\x01\xE0\x82\x01QR\x7F\x0E\xAB\xA9\xF4)\xC5\xF6\xFC1\x03\xD4\xCC@V\xC5\0\xFFBB]\x8Ede\xC5\xB8\xE1E!\x9F\x9C\\\xD3` a\x01\xE0\x83\x01Q\x01R\x7F)\xAE5\x1D\t\xDC\xF4\x1C\n\x80\xAB\x059785\x8B\xAA\xB3~o\xBCFK;\xB12X\x99J\x1F\xA4a\x02\0\x82\x01QR\x7F+{\xC7F\x08\xD7\xEC}\xAD\xD0Y}j@\x10\xD8\xBF\xC2\xB3\x19\0(\x19\x01\xCE\xDCB\xBD\xBB\x0F\xB8\xFC` a\x02\0\x83\x01Q\x01R\x7F\x06h\x02\xC7\xCE\xB9\xE9\x13\xD4\xF6T3\xA2\x06a\xE0\x97\xAC\xAC\x1A\xFF\xEC\xBBSJT\xF7j)x\"&a\x02 \x82\x01QR\x7F'\xEC\x80\xE8\x11\xE66\xF34\x82g\x92<\x8Ed\x1B\xD9\x8A~7\xC5!fp\xCB\xFF\x14\xAE2?\x9E\x0E` a\x02 \x83\x01Q\x01R\x7F\x12`M\x1F\x87\xC5\x83\xF6\xC9q\x0Cs\xEA\xF5\x90\xAF\x9D\x07\xAAt=\x13\x81\xD0\xE9\xDF\xF0\xEA\xB2aB9a\x02@\x82\x01QR\x7F\x15\x88W\x9El3x\xEA2\xCBd\x12\x05\xEFv*c\xCD5:\x0B\xD6p9E(\xAD \x81\xEE\x8D\xD4` a\x02@\x83\x01Q\x01R\x7F$}e&\x1D:J\xB0B\xBA\x93s1\xF6\xD0\xC0\xC5\xEB\x9E\xA7\x87S\xA9 \x84\xDB\x1Ai9\xE1\x9E\x82a\x02`\x82\x01QR\x7F,\xE6\xCCfJ2\x14{\xFEj\x0C\x94\xA9[\xF0Ify@\\\xCA\xE0\x16H\xCDN\xC0!\x14Q \xD5` a\x02`\x83\x01Q\x01R\x90V[`\0a\x1F\xD2\x82a!5V[a\x1F\xF5\x83`\0\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[` \x02` \x01\x01Qa\x10\x90V[a \x0B\x83`\x01\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a !\x83`\x02\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a 7\x83`\x03\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a M\x83`\x04\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a c\x83`\x05\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a y\x83`\x06\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a \x8F\x83`\x07\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[`\0a \x9C\x85\x85\x85a\"mV[\x90Pa \xA7\x81a#\xB7V[\x91PP[\x93\x92PPPV[a \xBB\x82a(\x16V[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a!\0Wa\x15\xB7\x82\x82a(rV[a\n\xC9a(\xE8V[a!\x10a)\x07V[a\x0BXW`@Qc\x1A\xFC\xD7\x9F`\xE3\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x10(a!\x08V[\x80Qa!@\x90a)!V[a!M\x81` \x01Qa)!V[a!Z\x81`@\x01Qa)!V[a!g\x81``\x01Qa)!V[a!t\x81`\x80\x01Qa)!V[a!\x81\x81`\xA0\x01Qa)!V[a!\x8E\x81`\xC0\x01Qa)!V[a!\x9B\x81`\xE0\x01Qa)!V[a!\xA9\x81a\x01\0\x01Qa)!V[a!\xB7\x81a\x01 \x01Qa)!V[a!\xC5\x81a\x01@\x01Qa)!V[a!\xD3\x81a\x01`\x01Qa)!V[a!\xE1\x81a\x01\x80\x01Qa)!V[a!\xEF\x81a\x01\xA0\x01Qa\x10\x90V[a!\xFD\x81a\x01\xC0\x01Qa\x10\x90V[a\"\x0B\x81a\x01\xE0\x01Qa\x10\x90V[a\"\x19\x81a\x02\0\x01Qa\x10\x90V[a\"'\x81a\x02 \x01Qa\x10\x90V[a\"5\x81a\x02@\x01Qa\x10\x90V[a\"C\x81a\x02`\x01Qa\x10\x90V[a\"Q\x81a\x02\x80\x01Qa\x10\x90V[a\"_\x81a\x02\xA0\x01Qa\x10\x90V[a\x10[\x81a\x02\xC0\x01Qa\x10\x90V[a\"uaN\xF6V[\x83` \x01Q\x83Q\x14a\"\x9AW`@Qc \xFA\x9D\x89`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\"\xA7\x85\x85\x85a)\xAFV[\x90P`\0a\"\xB8\x86`\0\x01Qa,\xCEV[\x90P`\0a\"\xCB\x82\x84`\xA0\x01Q\x88a0\x99V[`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x03\xC0\x806\x837PP`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[a#\x16aO0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a#\x0EW\x90PP\x90P`\0a#;\x8A\x85\x8A\x89\x87\x87a0\xF9V[`\xA0\x87\x01Q``\x87\x01Q\x91\x92P\x90`\0\x80Q` aYy\x839\x81Q\x91R`\0\x81\x83\x85\t`@\x80Qa\x01\0\x81\x01\x82R`\xE0\x9C\x8D\x01Q\x81R` \x81\x01\x96\x90\x96R\x85\x01RPPP``\x81\x01\x91\x90\x91R`\x80\x81\x01\x92\x90\x92R`\xA0\x82\x01Ra\x01`\x86\x01Q`\xC0\x82\x01Ra\x01\x80\x90\x95\x01Q\x92\x85\x01\x92\x90\x92RP\x91\x94\x93PPPPV[`\0`\0\x80Q` aYy\x839\x81Q\x91Ra#\xD0aO0V[a#\xD8aO0V[`@\x80Q`\x02\x80\x82R``\x82\x01\x83R`\0\x92` \x83\x01\x90\x806\x837PP`@\x80Q`\x02\x80\x82R``\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[a$\x1AaO0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a$\x12W\x90PP\x90P`\0`\x01\x90P\x80\x83`\0\x81Q\x81\x10a$IWa$IaU!V[` \x02` \x01\x01\x81\x81RPP\x87`\xC0\x01Q\x82`\0\x81Q\x81\x10a$mWa$maU!V[` \x02` \x01\x01\x81\x90RP\x87`\0\x01Q\x83`\x01\x81Q\x81\x10a$\x90Wa$\x90aU!V[` \x02` \x01\x01\x81\x81RPP\x87`\xE0\x01Q\x82`\x01\x81Q\x81\x10a$\xB4Wa$\xB4aU!V[` \x02` \x01\x01\x81\x90RPa$\xC9\x82\x84a1.V[`\x80\x89\x01QQ\x90\x95P``\x93P\x83\x92P\x90P`\0a$\xE8\x82`\x02aU\x87V[a$\xF3\x90`\x01aU\x87V[\x90P\x80`\x01`\x01`@\x1B\x03\x81\x11\x15a%\rWa%\raO\xCCV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a%6W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x80`\x01`\x01`@\x1B\x03\x81\x11\x15a%QWa%QaO\xCCV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a%\x8AW\x81` \x01[a%waO0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a%oW\x90P[P\x92PPP`\0\x80`\0[\x89`\x80\x01QQ\x81\x10\x15a&.W\x89`\x80\x01Q\x81\x81Q\x81\x10a%\xB8Wa%\xB8aU!V[` \x02` \x01\x01Q\x85\x83\x81Q\x81\x10a%\xD2Wa%\xD2aU!V[` \x02` \x01\x01\x81\x81RPP\x89`\xA0\x01Q\x81\x81Q\x81\x10a%\xF4Wa%\xF4aU!V[` \x02` \x01\x01Q\x84\x83\x81Q\x81\x10a&\x0EWa&\x0EaU!V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra&$`\x01\x83aU\x87V[\x91P`\x01\x01a%\x95V[P\x88` \x01Q\x84\x82\x81Q\x81\x10a&FWa&FaU!V[` \x02` \x01\x01\x81\x81RPP\x88`\xC0\x01Q\x83\x82\x81Q\x81\x10a&iWa&iaU!V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra&\x7F`\x01\x82aU\x87V[\x89Q`@\x8B\x01Q\x91\x92P\x90`\0\x89\x82\x84\t\x90P\x80\x87\x85\x81Q\x81\x10a&\xA5Wa&\xA5aU!V[` \x02` \x01\x01\x81\x81RPPPPP\x88`\xE0\x01Q\x83\x82\x81Q\x81\x10a&\xCBWa&\xCBaU!V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra&\xE1`\x01\x82aU\x87V[``\x8A\x01Q\x90\x91P\x87\x81\x84\x08\x92PPa&\xF9\x82a2\x1CV[\x84\x82\x81Q\x81\x10a'\x0BWa'\x0BaU!V[` \x02` \x01\x01\x81\x81RPPa'\x1Fa2NV[\x83\x82\x81Q\x81\x10a'1Wa'1aU!V[` \x02` \x01\x01\x81\x90RPa'Na'I\x84\x86a1.V[a2oV[\x94PPPPP`\0`@Q\x80`\x80\x01`@R\x80\x7F\x01\x18\xC4\xD5\xB87\xBC\xC2\xBC\x89\xB5\xB3\x98\xB5\x97N\x9FYD\x07;2\x07\x8B~#\x1F\xEC\x93\x88\x83\xB0\x81R` \x01\x7F&\x0E\x01\xB2Q\xF6\xF1\xC7\xE7\xFFNX\x07\x91\xDE\xE8\xEAQ\xD8z5\x8E\x03\x8BN\xFE0\xFA\xC0\x93\x83\xC1\x81R` \x01\x7F\"\xFE\xBD\xA3\xC0\xC0c*VG[B\x14\xE5a^\x11\xE6\xDD?\x96\xE6\xCE\xA2\x85J\x87\xD4\xDA\xCC^U\x81R` \x01\x7F\x04\xFCci\xF7\x11\x0F\xE3\xD2QV\xC1\xBB\x9Ar\x85\x9C\xF2\xA0FA\xF9\x9B\xA4\xEEA<\x80\xDAj_\xE4\x81RP\x90Pa(\x0C\x83\x82\x84a(\x07a2\xDCV[a3\xADV[\x96\x95PPPPPPV[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a(CW\x80`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\x08[\x91\x90aO\xB8V[`\0\x80Q` aYY\x839\x81Q\x91R\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa(\x8F\x91\x90aU\x9AV[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a(\xCAW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a(\xCFV[``\x91P[P\x91P\x91Pa(\xDF\x85\x83\x83a4\x90V[\x95\x94PPPPPV[4\x15a\x0BXW`@Qc\xB3\x98\x97\x9F`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a)\x11a\x16\x85V[T`\x01`@\x1B\x90\x04`\xFF\x16\x91\x90PV[`\0`\0\x80Q` aX\xF9\x839\x81Q\x91Ra);\x83a4\xE3V[\x15a)EWPPPV[\x82Q` \x84\x01Q\x82`\x03\x84\x85\x85\x86\t\x85\t\x08\x83\x82\x83\t\x14\x83\x82\x10\x84\x84\x10\x16\x16\x93PPP\x81a\x15\xB7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x17`$\x82\x01Rv\x10\x9B\x8C\x8DM\x0E\x88\x1A[\x9D\x98[\x1AY\x08\x11\xCCH\x1C\x1B\xDA[\x9D`J\x1B`D\x82\x01R`d\x01a\x08[V[a)\xF7`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@\x80Q\x80\x82\x01\x90\x91R``\x81R`\0` \x82\x01R`\0\x80Q` aYy\x839\x81Q\x91Ra*&\x82\x87\x87a4\xF2V[\x81Q\x84Qa*3\x90a8\x8BV[a*@\x86` \x01Qa8\x8BV[a*M\x87`@\x01Qa8\x8BV[a*Z\x88``\x01Qa8\x8BV[a*g\x89`\x80\x01Qa8\x8BV[`@Q` \x01a*|\x96\x95\x94\x93\x92\x91\x90aU\xB6V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra*\x97\x82a9\0V[Pa*\xA1\x82a9\0V[``\x84\x01Ra*\xAF\x82a9\0V[`\x80\x84\x01R\x81Q`\xA0\x85\x01Qa*\xC4\x90a8\x8BV[`@Q` \x01a*\xD5\x92\x91\x90aV5V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra*\xF0\x82a9\0V[\x83R\x81Q`\xC0\x85\x01Qa+\x02\x90a8\x8BV[a+\x0F\x86`\xE0\x01Qa8\x8BV[a+\x1D\x87a\x01\0\x01Qa8\x8BV[a++\x88a\x01 \x01Qa8\x8BV[a+9\x89a\x01@\x01Qa8\x8BV[`@Q` \x01a+N\x96\x95\x94\x93\x92\x91\x90aU\xB6V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra+i\x82a9\0V[`\xA0\x84\x01R\x81Qa\x01\xA0\x85\x01Qa+\x7F\x90a9bV[a+\x8D\x86a\x01\xC0\x01Qa9bV[a+\x9B\x87a\x01\xE0\x01Qa9bV[a+\xA9\x88a\x02\0\x01Qa9bV[a+\xB7\x89a\x02 \x01Qa9bV[`@Q` \x01a+\xCC\x96\x95\x94\x93\x92\x91\x90aVdV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x83Ra\x02@\x85\x01Qa+\xEE\x90a9bV[a+\xFC\x86a\x02`\x01Qa9bV[a,\n\x87a\x02\x80\x01Qa9bV[a,\x18\x88a\x02\xA0\x01Qa9bV[a,&\x89a\x02\xC0\x01Qa9bV[`@Q` \x01a,;\x96\x95\x94\x93\x92\x91\x90aVdV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,V\x82a9\0V[`\xC0\x84\x01R\x81Qa\x01`\x85\x01Qa,l\x90a8\x8BV[a,z\x86a\x01\x80\x01Qa8\x8BV[`@Q` \x01a,\x8C\x93\x92\x91\x90aV\xA0V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\xA7\x82a9\0V[`\xE0\x84\x01R\x82Q\x81\x81\x80\t\x82\x82\x82\t` \x86\x01\x91\x90\x91R`@\x85\x01RP\x91\x95\x94PPPPPV[a-\0`@Q\x80`\xA0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[\x81b\x01\0\0\x03a-\x94WP`@\x80Q`\xA0\x81\x01\x82R`\x10\x81R` \x81\x01\x92\x90\x92R\x7F0d\x1E\x0E\x92\xBE\xBE\xF8\x18&\x8Df;\xCA\xD6\xDB\xCF\xD6\xC0\x14\x91p\xF6\xD7\xD3P\xB1\xB1\xFAl\x10\x01\x90\x82\x01R~\xEE\xB2\xCBY\x81\xEDEd\x9A\xBE\xBD\xE0\x81\xDC\xFF\x16\xC8`\x1D\xE44~}\xD1b\x8B\xA2\xDA\xACC\xB7``\x82\x01R\x7F\x0B]V\xB7\x7F\xE7\x04\xE8\xE9#8\xC0\x08/7\xE0\x91\x12d\x14\xC80\xE4\xC6\x92-Z\xC8\x02\xD8B\xD4`\x80\x82\x01R\x90V[\x81b\x02\0\0\x03a.)WP`@\x80Q`\xA0\x81\x01\x82R`\x11\x81R` \x81\x01\x92\x90\x92R\x7F0d6@\xB9\xF8/\x90\xE8;i\x8E^\xA6\x17\x9C|\x05T.\x85\x953\xB4\x8B\x99S\xA2\xF56\x08\x01\x90\x82\x01R\x7F\x1B\xF8-\xEB\xA7\xD7I\x02\xC3p\x8C\xC6\xE7\x0Ea\xF3\x05\x12\xEC\xA9VU!\x0E'nXX\xCE\x8FX\xE5``\x82\x01R\x7F$L\xF0\x10\xC4<\xA8r7\xD8\xB0\x0B\xF9\xDDP\xC4\xC0\x1C\x7F\x08k\xD4\xE8\xC9 \xE7RQ\xD9o\r\"`\x80\x82\x01R\x90V[\x81b\x04\0\0\x03a.\xBEWP`@\x80Q`\xA0\x81\x01\x82R`\x12\x81R` \x81\x01\x92\x90\x92R\x7F0dBY\xCD\x94\xE7\xDDPE\xD7\xA2p\x13\xB7\xFC\xD2\x1C\x9E;\x7F\xA7R\"\xE7\xBD\xA4\x9Br\x9B\x04\x01\x90\x82\x01R\x7F\x19\xDD\xBC\xAF:\x8DF\xC1\\\x01v\xFB\xB5\xB9^M\xC5p\x88\xFF\x13\xF4\xD1\xBD\x84\xC6\xBF\xA5}\xCD\xC0\xE0``\x82\x01R\x7F\x03hS\xF0\x83x\x0E\x87\xF8\xD7\xC7\x1D\x11\x11\x19\xC5}\xBE\x11\x8C\"\xD5\xADpz\x821tf\xC5\x17L`\x80\x82\x01R\x90V[\x81b\x08\0\0\x03a/SWP`@\x80Q`\xA0\x81\x01\x82R`\x13\x81R` \x81\x01\x92\x90\x92R\x7F0dHfWcD\x03\x84K\x0E\xACx\xCA\x88,\xFD(CA\xFC\xB0aZ\x15\xCF\xCD\x17\xB1M\x82\x01\x90\x82\x01R\x7F\"`\xE7$\x84K\xCARQ\x82\x93S\x96\x8EI\x150RXA\x83WG:\\\x1DY\x7Fa?l\xBD``\x82\x01R\x7F\x06\xE4\x02\xC0\xA3\x14\xFBg\xA1\\\xF8\x06fJ\xE1\xB7\"\xDB\xC0\xEF\xE6nl\x81\xD9\x8F\x99$\xCASS!`\x80\x82\x01R\x90V[\x81b\x10\0\0\x03a/\xE8WP`@\x80Q`\xA0\x81\x01\x82R`\x14\x81R` \x81\x01\x92\x90\x92R\x7F0dKl\x9CJr\x16\x9EM\xAA1}%\xF0E\x12\xAE\x15\xC5;4\xE8\xF5\xAC\xD8\xE1U\xD0\xA6\xC1\x01\x90\x82\x01R\x7F&\x12]\xA1\n\x0E\xD0c'P\x8A\xBA\x06\xD1\xE3\x03\xACaf2\xDB\xED4\x9FSB-\xA9S3xW``\x82\x01R\x7F\x10\x0C3-!\0\x89_\xABds\xBC,Q\xBF\xCAR\x1FE\xCB;\xAC\xA6&\x08R\xA8\xFD\xE2l\x91\xF3`\x80\x82\x01R\x90V[\x81` \x03a0{WP`@\x80Q`\xA0\x81\x01\x82R`\x05\x81R` \x81\x01\x92\x90\x92R\x7F.\xE1+\xFFJ(\x13(j\x8D\xC3\x88\xCDuM\x9A>\xF2I\x065\xEB\xA5\x0C\xB9\xC2\xE5\xE7P\x80\0\x01\x90\x82\x01R\x7F\t\xC52\xC60k\x93\xD2\x96x \rG\xC0\xB2\xA9\x9C\x18\xD5\x1B\x83\x8E\xEB\x1D>\xEDLS;\xB5\x12\xD0``\x82\x01R\x7F'$q6\x03\xBF\xBDy\n\xEA\xF3\xE7\xDF%\xD8\xE7\xEF\x8F1\x134\x90[M\x8C\x99\x98\x0C\xF2\x10\x97\x9D`\x80\x82\x01R\x90V[`@Qc\xE2\xEF\t\xE5`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x91\x90PV[a0\xBD`@Q\x80``\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[a0\xC7\x84\x84a:\x99V[\x80\x82Ra0\xD7\x90\x85\x90\x85\x90a:\xEDV[` \x82\x01R\x80Qa0\xED\x90\x85\x90\x84\x90\x86\x90a;aV[`@\x82\x01R\x93\x92PPPV[`\0\x80a1\x07\x85\x87\x89a=\x15V[\x90Pa1\x17\x88\x86\x89\x89\x88\x88a>\x01V[a1\"\x81\x87\x86aA\rV[\x98\x97PPPPPPPPV[a16aO0V[\x82Q\x82Q\x14a1\x87W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FMSM error: length does not match`D\x82\x01R`d\x01a\x08[V[a1\xC5\x83`\0\x81Q\x81\x10a1\x9DWa1\x9DaU!V[` \x02` \x01\x01Q\x83`\0\x81Q\x81\x10a1\xB8Wa1\xB8aU!V[` \x02` \x01\x01QaA]V[\x90P`\x01[\x82Q\x81\x10\x15a2\x15Wa2\x0B\x82a2\x06\x86\x84\x81Q\x81\x10a1\xECWa1\xECaU!V[` \x02` \x01\x01Q\x86\x85\x81Q\x81\x10a1\xB8Wa1\xB8aU!V[aA\xF1V[\x91P`\x01\x01a1\xCAV[P\x92\x91PPV[`\0a26`\0\x80Q` aYy\x839\x81Q\x91R\x83aV\xF9V[a\x10\x1A\x90`\0\x80Q` aYy\x839\x81Q\x91RaU\x0EV[a2VaO0V[P`@\x80Q\x80\x82\x01\x90\x91R`\x01\x81R`\x02` \x82\x01R\x90V[a2waO0V[a2\x80\x82a4\xE3V[\x15a2\x89WP\x90V[`@Q\x80`@\x01`@R\x80\x83`\0\x01Q\x81R` \x01`\0\x80Q` aX\xF9\x839\x81Q\x91R\x84` \x01Qa2\xBC\x91\x90aV\xF9V[a2\xD4\x90`\0\x80Q` aX\xF9\x839\x81Q\x91RaU\x0EV[\x90R\x92\x91PPV[a3\x07`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80`\x80\x01`@R\x80\x7F\x18\0\xDE\xEF\x12\x1F\x1EvBj\0f^\\DygC\"\xD4\xF7^\xDA\xDDF\xDE\xBD\\\xD9\x92\xF6\xED\x81R` \x01\x7F\x19\x8E\x93\x93\x92\rH:r`\xBF\xB71\xFB]%\xF1\xAAI35\xA9\xE7\x12\x97\xE4\x85\xB7\xAE\xF3\x12\xC2\x81R` \x01\x7F\x12\xC8^\xA5\xDB\x8Cm\xEBJ\xABq\x80\x8D\xCB@\x8F\xE3\xD1\xE7i\x0CC\xD3{L\xE6\xCC\x01f\xFA}\xAA\x81R` \x01\x7F\t\x06\x89\xD0X_\xF0u\xEC\x9E\x99\xADi\x0C3\x95\xBCK13p\xB3\x8E\xF3U\xAC\xDA\xDC\xD1\"\x97[\x81RP\x90P\x90V[`\0\x80`\0`@Q\x87Q\x81R` \x88\x01Q` \x82\x01R` \x87\x01Q`@\x82\x01R\x86Q``\x82\x01R``\x87\x01Q`\x80\x82\x01R`@\x87\x01Q`\xA0\x82\x01R\x85Q`\xC0\x82\x01R` \x86\x01Q`\xE0\x82\x01R` \x85\x01Qa\x01\0\x82\x01R\x84Qa\x01 \x82\x01R``\x85\x01Qa\x01@\x82\x01R`@\x85\x01Qa\x01`\x82\x01R` `\0a\x01\x80\x83`\x08Z\xFA\x91PP`\0Q\x91P\x80a4\x82W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1C`$\x82\x01R{Bn254: Pairing check failed!` \x1B`D\x82\x01R`d\x01a\x08[V[P\x15\x15\x90P[\x94\x93PPPPV[``\x82a4\xA5Wa4\xA0\x82aB\x8CV[a \xABV[\x81Q\x15\x80\x15a4\xBCWP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a4\xDCW\x83`@Qc\x99\x96\xB3\x15`\xE0\x1B\x81R`\x04\x01a\x08[\x91\x90aO\xB8V[P\x80a \xABV[\x80Q` \x90\x91\x01Q\x15\x90\x15\x16\x90V[\x82Q`\xFE\x90a5-a5\x03\x83a9bV[`@Q` \x01a5\x15\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x04aB\xB5V[a5ga5=\x86`\0\x01Qa9bV[`@Q` \x01a5O\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x08aB\xB5V[a5wa5=\x87` \x01Qa9bV[`@Q` \x01a5\x8A\x94\x93\x92\x91\x90aW\x1BV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85Ra5\xA7`\x01a9bV[a5\xBE`\0\x80Q` aZ\x19\x839\x81Q\x91Ra9bV[a5\xD5`\0\x80Q` aY\xB9\x839\x81Q\x91Ra9bV[a5\xEC`\0\x80Q` aY\xF9\x839\x81Q\x91Ra9bV[a6\x03`\0\x80Q` aY9\x839\x81Q\x91Ra9bV[`@Q` \x01a6\x18\x96\x95\x94\x93\x92\x91\x90aVdV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85R`\xE0\x84\x01Qa69\x90a8\x8BV[a6G\x85a\x01\0\x01Qa8\x8BV[a6U\x86a\x01 \x01Qa8\x8BV[a6c\x87a\x01@\x01Qa8\x8BV[a6q\x88a\x01`\x01Qa8\x8BV[a6\x7F\x89a\x01\x80\x01Qa8\x8BV[a6\x8D\x8Aa\x01\xE0\x01Qa8\x8BV[`@Q` \x01a6\xA4\x98\x97\x96\x95\x94\x93\x92\x91\x90aWrV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85Ra\x02\0\x84\x01Qa6\xC6\x90a8\x8BV[a6\xD4\x85a\x02 \x01Qa8\x8BV[a6\xE2\x86a\x02@\x01Qa8\x8BV[a6\xF0\x87a\x01\xA0\x01Qa8\x8BV[a6\xFE\x88a\x01\xC0\x01Qa8\x8BV[a7\x0C\x89a\x02`\x01Qa8\x8BV[`@Q` \x01a7\"\x97\x96\x95\x94\x93\x92\x91\x90aX\x17V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x81R\x81\x86R\x84\x01Qa7A\x90a8\x8BV[a7N\x85``\x01Qa8\x8BV[a7[\x86`\x80\x01Qa8\x8BV[a7h\x87`\xA0\x01Qa8\x8BV[a7u\x88`\xC0\x01Qa8\x8BV[`@Q` \x01a7\x8A\x96\x95\x94\x93\x92\x91\x90aU\xB6V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85R\x82Qa7\xC1\x90\x84\x90`\0\x90a7\xB4Wa7\xB4aU!V[` \x02` \x01\x01Qa9bV[a7\xD7\x84`\x01\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a7\xED\x85`\x02\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8\x03\x86`\x03\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8\x19\x87`\x04\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8/\x88`\x05\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8E\x89`\x06\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8[\x8A`\x07\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[`@Q` \x01a8s\x99\x98\x97\x96\x95\x94\x93\x92\x91\x90aX\xA9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x90\x93RPPPV[```\0a8\x98\x83a4\xE3V[\x15a8\xA4W`\x01`\xFE\x1B\x17[` \x83\x01Q`\0\x80Q` aX\xF9\x839\x81Q\x91R`\x01\x91\x90\x91\x1B\x10a8\xCAWP`\x01`\xFF\x1B[\x82Qa8\xD7\x90\x82\x17a9bV[`@Q` \x01a8\xE9\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP\x91\x90PV[` \x80\x82\x01Q\x82Q\x80Q`@Q\x83\x81R`\0\x94\x85\x94\x93\x92\x91\x90\x81\x01\x85[\x83\x81\x10\x15a97W` \x81\x86\x01\x81\x01Q\x83\x83\x01R\x01a9\x1DV[PP` \x91\x82\x01\x90 \x90\x86\x01\x81\x90R\x92P`\0a(\x0C`\0\x80Q` aYy\x839\x81Q\x91R\x85aV\xF9V[`\0\x81\x90P`\x08\x81~\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\x16\x90\x1B`\x08\x82\x7F\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\x16\x90\x1C\x17\x90P`\x10\x81}\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\x16\x90\x1B`\x10\x82}\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\x19\x16\x90\x1C\x17\x90P` \x81{\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\x16\x90\x1B` \x82{\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\x19\x16\x90\x1C\x17\x90P`@\x81`\x01`\x01`@\x1B\x03`\x01`\x80\x1B\x03`\x01`\xC0\x1B\x03\x16\x90\x1B`@\x82`\x01`\x01`@\x1B\x03`\x01`\x80\x1B\x03`\x01`\xC0\x1B\x03\x19\x16\x90\x1C\x17\x90P`\x80\x81\x90\x1B`\x80\x82\x90\x1C\x17\x90P\x91\x90PV[\x81Q`\0\x90`\0\x80Q` aYy\x839\x81Q\x91R\x90\x83\x80\x15a:\xDDW\x84\x93P`\0[\x82\x81\x10\x15a:\xD1W\x83\x85\x86\t\x94P`\x01\x01a:\xBBV[P`\x01\x84\x03\x93Pa:\xE4V[`\x01\x83\x03\x93P[PPP\x92\x91PPV[`\0\x82`\x01\x03a:\xFFWP`\x01a \xABV[\x81`\0\x03a;\x0FWP`\0a \xABV[`@\x84\x01Q`\0\x80Q` aYy\x839\x81Q\x91R\x90`\0\x90\x82\x81\x86\t\x90P\x85\x80\x15a;?W`\x01\x87\x03\x92Pa;FV[`\x01\x84\x03\x92P[Pa;P\x82aC\xC2V[\x91P\x82\x82\x82\t\x97\x96PPPPPPPV[\x82Q`\0\x90`\0\x80Q` aYy\x839\x81Q\x91R\x90\x83\x83\x03a;\xE2W`\x01`\0[\x82\x81\x10\x15a;\xD5W\x81\x87\x03a;\xB6W\x87\x81\x81Q\x81\x10a;\xA3Wa;\xA3aU!V[` \x02` \x01\x01Q\x94PPPPPa4\x88V[\x83\x80a;\xC4Wa;\xC4aV\xE3V[\x89``\x01Q\x83\t\x91P`\x01\x01a;\x82V[P`\0\x93PPPPa4\x88V[`\0\x80`\0\x80\x8A`@\x01Q\x90P`\0\x80a;\xFC\x8D\x88aDhV[\x90P`\0\x87`\x01`\x01`@\x1B\x03\x81\x11\x15a<\x18Wa<\x18aO\xCCV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F\x86\x86\x86\x86\x85\x87aE-V[`\xC0\x85\x01Q\x82Q`\0\x80Q` aYy\x839\x81Q\x91R\x91\x90\x81\x90\x81\x90\x86\x90`\x14\x90\x81\x10a>>Wa>>aU!V[` \x02` \x01\x01\x81\x81RPP\x85`\0\x01Q\x84`\x14\x81Q\x81\x10a>bWa>baU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x15\x81Q\x81\x10a>\x87Wa>\x87aU!V[` \x02` \x01\x01\x81\x81RPP\x85` \x01Q\x84`\x15\x81Q\x81\x10a>\xABWa>\xABaU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x16\x81Q\x81\x10a>\xD0Wa>\xD0aU!V[` \x02` \x01\x01\x81\x81RPP\x85`@\x01Q\x84`\x16\x81Q\x81\x10a>\xF4Wa>\xF4aU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x17\x81Q\x81\x10a?\x19Wa?\x19aU!V[` \x02` \x01\x01\x81\x81RPP\x85``\x01Q\x84`\x17\x81Q\x81\x10a?=Wa?=aU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x18\x81Q\x81\x10a?bWa?baU!V[` \x02` \x01\x01\x81\x81RPP\x85`\x80\x01Q\x84`\x18\x81Q\x81\x10a?\x86Wa?\x86aU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x19\x81Q\x81\x10a?\xABWa?\xABaU!V[` \x02` \x01\x01\x81\x81RPP\x88`@\x01Q\x84`\x19\x81Q\x81\x10a?\xCFWa?\xCFaU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1A\x81Q\x81\x10a?\xF4Wa?\xF4aU!V[` \x02` \x01\x01\x81\x81RPP\x88``\x01Q\x84`\x1A\x81Q\x81\x10a@\x18Wa@\x18aU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1B\x81Q\x81\x10a@=Wa@=aU!V[` \x02` \x01\x01\x81\x81RPP\x88`\x80\x01Q\x84`\x1B\x81Q\x81\x10a@aWa@aaU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1C\x81Q\x81\x10a@\x86Wa@\x86aU!V[` \x02` \x01\x01\x81\x81RPP\x88`\xA0\x01Q\x84`\x1C\x81Q\x81\x10a@\xAAWa@\xAAaU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x87`\xE0\x01Q\x85`\x1D\x81Q\x81\x10a@\xD3Wa@\xD3aU!V[` \x02` \x01\x01\x81\x81RPP\x85`\xA0\x01Q\x84`\x1D\x81Q\x81\x10a@\xF7Wa@\xF7aU!V[` \x02` \x01\x01\x81\x90RPPPPPPPPPPV[`\0\x80Q` aYy\x839\x81Q\x91R\x83\x81\x03\x90`\0[`\n\x81\x10\x15aATW` `\x15\x82\x01\x02\x84\x01Q` \x82\x02a\x01\xA0\x01\x86\x01Q\x83\x84\x82\x84\t\x86\x08\x94PPP`\x01\x01aA#V[PP\x93\x92PPPV[aAeaO0V[aAmaOJV[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x81\x01\x83\x90R`\0``\x83`\x80\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80aA\x9FW`\0\x80\xFD[P\x80aA\xE9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01RxBn254: scalar mul failed!`8\x1B`D\x82\x01R`d\x01a\x08[V[PP\x92\x91PPV[aA\xF9aO0V[aB\x01aOhV[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x83\x01R\x83\x01Q``\x80\x83\x01\x91\x90\x91R`\0\x90\x83`\xC0\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80aB>W`\0\x80\xFD[P\x80aA\xE9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: group addition failed!\0\0\0`D\x82\x01R`d\x01a\x08[V[\x80Q\x15aB\x9CW\x80Q\x80\x82` \x01\xFD[`@Qc\n\x12\xF5!`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x81aB\xC3\x81`\x1FaU\x87V[\x10\x15aC\x02W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x0E`$\x82\x01Rmslice_overflow`\x90\x1B`D\x82\x01R`d\x01a\x08[V[aC\x0C\x82\x84aU\x87V[\x84Q\x10\x15aCPW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x11`$\x82\x01Rpslice_outOfBounds`x\x1B`D\x82\x01R`d\x01a\x08[V[``\x82\x15\x80\x15aCoW`@Q\x91P`\0\x82R` \x82\x01`@RaC\xB9V[`@Q\x91P`\x1F\x84\x16\x80\x15` \x02\x81\x84\x01\x01\x85\x81\x01\x87\x83\x15` \x02\x84\x8B\x01\x01\x01[\x81\x83\x10\x15aC\xA8W\x80Q\x83R` \x92\x83\x01\x92\x01aC\x90V[PP\x85\x84R`\x1F\x01`\x1F\x19\x16`@RP[P\x94\x93PPPPV[`\0\x80`\0`\0\x80Q` aYy\x839\x81Q\x91R\x90P`@Q` \x81R` \x80\x82\x01R` `@\x82\x01R\x84``\x82\x01R`\x02\x82\x03`\x80\x82\x01R\x81`\xA0\x82\x01R` `\0`\xC0\x83`\x05Z\xFA\x92PP`\0Q\x92P\x81aDaW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: pow precompile failed!\0\0\0`D\x82\x01R`d\x01a\x08[V[PP\x91\x90PV[``\x82` \x01Q\x82\x11\x15aD\x8FW`@Qc\x8C^\x11\xF1`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x83\x01Q`\x01`\0\x80Q` aYy\x839\x81Q\x91R\x84`\x01`\x01`@\x1B\x03\x81\x11\x15aD\xBDWaD\xBDaO\xCCV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15aD\xE6W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x84\x15\x19\x15a:\xE4W` \x84\x01\x85` \x02\x81\x01`\x01\x82R` \x82\x01\x91P[\x80\x82\x10\x15aE\"W\x82\x85\x85\t\x93P\x83\x82R` \x82\x01\x91PaE\x06V[PPPPP\x92\x91PPV[`\0\x80`\0\x80`\0\x80`\0\x80Q` aYy\x839\x81Q\x91R\x90P\x80` \x8B\x01Q` \x8D\x01Q\t\x95P\x8AQ\x93P\x80`\xA0\x8C\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` aZ\x19\x839\x81Q\x91R\x84\t\x91P\x80a\x01\xC0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` aY\xB9\x839\x81Q\x91R\x84\t\x91P\x80a\x01\xE0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` aY\xF9\x839\x81Q\x91R\x84\t\x91P\x80a\x02\0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` aY9\x839\x81Q\x91R\x84\t\x91P\x80a\x02 \x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80\x84\x87\x08\x95P\x88`\xA0\x01Q\x88`\0\x81Q\x81\x10aF\\WaF\\aU!V[` \x02` \x01\x01\x81\x90RP\x85\x87`\0\x81Q\x81\x10aF{WaF{aU!V[` \x02` \x01\x01\x81\x81RPP\x80``\x8C\x01Q\x8CQ\t\x94P\x80a\x02\xC0\x8A\x01Q\x86\t\x94P\x80a\x02@\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02`\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xC0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02\x80\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xE0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02\xA0\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x02\0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x8B`\xC0\x01Q\x88`\x01\x81Q\x81\x10aG]WaG]aU!V[` \x90\x81\x02\x91\x90\x91\x01\x01RaGr\x85\x82aU\x0EV[\x87`\x01\x81Q\x81\x10aG\x85WaG\x85aU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xA0\x01Q\x87`\x02\x81Q\x81\x10aG\xAAWaG\xAAaU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xC0\x01Q\x87`\x03\x81Q\x81\x10aG\xCFWaG\xCFaU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xE0\x01Q\x87`\x04\x81Q\x81\x10aG\xF4WaG\xF4aU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x02\0\x01Q\x87`\x05\x81Q\x81\x10aH\x19WaH\x19aU!V[` \x02` \x01\x01\x81\x81RPP\x8B`\xE0\x01Q\x88`\x02\x81Q\x81\x10aH=WaH=aU!V[` \x02` \x01\x01\x81\x90RP\x8Ba\x01\0\x01Q\x88`\x03\x81Q\x81\x10aHaWaHaaU!V[` \x02` \x01\x01\x81\x90RP\x8Ba\x01 \x01Q\x88`\x04\x81Q\x81\x10aH\x85WaH\x85aU!V[` \x02` \x01\x01\x81\x90RP\x8Ba\x01@\x01Q\x88`\x05\x81Q\x81\x10aH\xA9WaH\xA9aU!V[` \x02` \x01\x01\x81\x90RP\x80a\x01\xC0\x8A\x01Qa\x01\xA0\x8B\x01Q\t\x92P\x82\x87`\x06\x81Q\x81\x10aH\xD8WaH\xD8aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01`\x01Q\x88`\x06\x81Q\x81\x10aH\xFDWaH\xFDaU!V[` \x02` \x01\x01\x81\x90RP\x80a\x02\0\x8A\x01Qa\x01\xE0\x8B\x01Q\t\x92P\x82\x87`\x07\x81Q\x81\x10aI,WaI,aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\x80\x01Q\x88`\x07\x81Q\x81\x10aIQWaIQaU!V[` \x02` \x01\x01\x81\x90RPa\x01\xA0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\x08\x81Q\x81\x10aI\x8AWaI\x8AaU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\xE0\x01Q\x88`\x08\x81Q\x81\x10aI\xAFWaI\xAFaU!V[` \x02` \x01\x01\x81\x90RPa\x01\xC0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\t\x81Q\x81\x10aI\xE8WaI\xE8aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02\0\x01Q\x88`\t\x81Q\x81\x10aJ\rWaJ\raU!V[` \x02` \x01\x01\x81\x90RPa\x01\xE0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\n\x81Q\x81\x10aJFWaJFaU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02 \x01Q\x88`\n\x81Q\x81\x10aJkWaJkaU!V[` \x02` \x01\x01\x81\x90RPa\x02\0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\x0B\x81Q\x81\x10aJ\xA4WaJ\xA4aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02@\x01Q\x88`\x0B\x81Q\x81\x10aJ\xC9WaJ\xC9aU!V[` \x02` \x01\x01\x81\x90RP\x88a\x02 \x01Q\x81aJ\xE5\x91\x90aU\x0EV[\x87`\x0C\x81Q\x81\x10aJ\xF8WaJ\xF8aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\xA0\x01Q\x88`\x0C\x81Q\x81\x10aK\x1DWaK\x1DaU!V[` \x02` \x01\x01\x81\x90RP`\x01\x87`\r\x81Q\x81\x10aK=WaK=aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\xC0\x01Q\x88`\r\x81Q\x81\x10aKbWaKbaU!V[` \x02` \x01\x01\x81\x90RP\x80a\x01\xC0\x8A\x01Qa\x01\xA0\x8B\x01Q\t\x92P\x80a\x01\xE0\x8A\x01Q\x84\t\x92P\x80a\x02\0\x8A\x01Q\x84\t\x92P\x80a\x02 \x8A\x01Q\x84\t\x92P\x82\x87`\x0E\x81Q\x81\x10aK\xB2WaK\xB2aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02`\x01Q\x88`\x0E\x81Q\x81\x10aK\xD7WaK\xD7aU!V[` \x90\x81\x02\x91\x90\x91\x01\x01R\x89QaK\xEE\x90\x82aU\x0EV[\x87`\x0F\x81Q\x81\x10aL\x01WaL\x01aU!V[` \x02` \x01\x01\x81\x81RPP\x88`\xC0\x01Q\x88`\x0F\x81Q\x81\x10aL%WaL%aU!V[` \x02` \x01\x01\x81\x90RP\x80`\x01\x8BQ\x08`\xA0\x8C\x01Q\x90\x93P\x81\x90\x80\t\x91P\x80\x82\x84\t\x92P\x80\x83` `\x10\x02\x89\x01Q\t\x91P\x81\x87`\x10\x81Q\x81\x10aLkWaLkaU!V[` \x02` \x01\x01\x81\x81RPP\x88`\xE0\x01Q\x88`\x10\x81Q\x81\x10aL\x8FWaL\x8FaU!V[` \x02` \x01\x01\x81\x90RP\x80\x83` `\x11\x02\x89\x01Q\t\x91P\x81\x87`\x11\x81Q\x81\x10aL\xBBWaL\xBBaU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01\0\x01Q\x88`\x11\x81Q\x81\x10aL\xE0WaL\xE0aU!V[` \x02` \x01\x01\x81\x90RP\x80\x83` `\x12\x02\x89\x01Q\t\x91P\x81\x87`\x12\x81Q\x81\x10aM\x0CWaM\x0CaU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01 \x01Q\x88`\x12\x81Q\x81\x10aM1WaM1aU!V[` \x02` \x01\x01\x81\x90RP\x80\x83` `\x13\x02\x89\x01Q\t\x91P\x81\x87`\x13\x81Q\x81\x10aM]WaM]aU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01@\x01Q\x88`\x13\x81Q\x81\x10aM\x82WaM\x82aU!V[` \x02` \x01\x01\x81\x90RPPPPPPPPPPPPPV[`@Q\x80a\x01\0\x01`@R\x80`\0`\x01`\x01`@\x1B\x03\x16\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x16\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80a\x02\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01aN\x14aO0V[\x81R` \x01aN!aO0V[\x81R` \x01aN.aO0V[\x81R` \x01aN;aO0V[\x81R` \x01aNHaO0V[\x81R` \x01aNUaO0V[\x81R` \x01aNbaO0V[\x81R` \x01aNoaO0V[\x81R` \x01aN|aO0V[\x81R` \x01aN\x89aO0V[\x81R` \x01aN\x96aO0V[\x81R` \x01aN\xA3aO0V[\x81R` \x01aN\xB0aO0V[\x81R` \x01aN\xBDaO0V[\x81R` \x01aN\xCAaO0V[\x81R` \x01aN\xD7aO0V[\x81R` \x01aN\xE4aO0V[\x81R` \x01aN\xF1aO0V[\x90R\x90V[`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01``\x81R` \x01``\x81R` \x01aN\xE4[`@Q\x80`@\x01`@R\x80`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80``\x01`@R\x80`\x03\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`\x80\x01`@R\x80`\x04\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a0\x94W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aO\xAFW`\0\x80\xFD[a \xAB\x82aO\x86V[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Qa\x02\xE0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aP\x05WaP\x05aO\xCCV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aP3WaP3aO\xCCV[`@R\x91\x90PV[\x805`\x01`\x01`@\x1B\x03\x81\x16\x81\x14a0\x94W`\0\x80\xFD[`\0a\x01\0\x80\x83\x85\x03\x12\x15aPfW`\0\x80\xFD[`@Q\x90\x81\x01\x90`\x01`\x01`@\x1B\x03\x82\x11\x81\x83\x10\x17\x15aP\x88WaP\x88aO\xCCV[\x81`@R\x80\x92PaP\x98\x84aP;V[\x81RaP\xA6` \x85\x01aP;V[` \x82\x01R`@\x84\x015`@\x82\x01R``\x84\x015``\x82\x01R`\x80\x84\x015`\x80\x82\x01R`\xA0\x84\x015`\xA0\x82\x01R`\xC0\x84\x015`\xC0\x82\x01R`\xE0\x84\x015`\xE0\x82\x01RPP\x92\x91PPV[`\0`@\x82\x84\x03\x12\x15aQ\x01W`\0\x80\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aQ#WaQ#aO\xCCV[`@R\x825\x81R` \x92\x83\x015\x92\x81\x01\x92\x90\x92RP\x91\x90PV[`\0\x80\x82\x84\x03a\x05\x80\x81\x12\x15aQRW`\0\x80\xFD[aQ\\\x85\x85aPRV[\x92Pa\x01\0a\x04\x80\x80`\xFF\x19\x84\x01\x12\x15aQuW`\0\x80\xFD[aQ}aO\xE2V[\x92PaQ\x8B\x87\x83\x88\x01aP\xEFV[\x83Ra\x01@aQ\x9C\x88\x82\x89\x01aP\xEFV[` \x85\x01Ra\x01\x80aQ\xB0\x89\x82\x8A\x01aP\xEFV[`@\x86\x01Ra\x01\xC0aQ\xC4\x8A\x82\x8B\x01aP\xEFV[``\x87\x01Ra\x02\0aQ\xD8\x8B\x82\x8C\x01aP\xEFV[`\x80\x88\x01Ra\x02@aQ\xEC\x8C\x82\x8D\x01aP\xEFV[`\xA0\x89\x01Ra\x02\x80aR\0\x8D\x82\x8E\x01aP\xEFV[`\xC0\x8A\x01Ra\x02\xC0aR\x14\x8E\x82\x8F\x01aP\xEFV[`\xE0\x8B\x01RaR'\x8Ea\x03\0\x8F\x01aP\xEFV[\x89\x8B\x01RaR9\x8Ea\x03@\x8F\x01aP\xEFV[a\x01 \x8B\x01RaRM\x8Ea\x03\x80\x8F\x01aP\xEFV[\x87\x8B\x01RaR_\x8Ea\x03\xC0\x8F\x01aP\xEFV[a\x01`\x8B\x01RaRs\x8Ea\x04\0\x8F\x01aP\xEFV[\x86\x8B\x01Ra\x04@\x8D\x015a\x01\xA0\x8B\x01Ra\x04`\x8D\x015\x85\x8B\x01R\x87\x8D\x015a\x01\xE0\x8B\x01Ra\x04\xA0\x8D\x015\x84\x8B\x01Ra\x04\xC0\x8D\x015a\x02 \x8B\x01Ra\x04\xE0\x8D\x015\x83\x8B\x01Ra\x05\0\x8D\x015a\x02`\x8B\x01Ra\x05 \x8D\x015\x82\x8B\x01Ra\x05@\x8D\x015a\x02\xA0\x8B\x01Ra\x05`\x8D\x015\x81\x8B\x01RPPPPPPPPP\x80\x91PP\x92P\x92\x90PV[`\0\x80`@\x83\x85\x03\x12\x15aS\nW`\0\x80\xFD[aS\x13\x83aO\x86V[\x91P` \x83\x81\x015`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aS0W`\0\x80\xFD[\x81\x86\x01\x91P\x86`\x1F\x83\x01\x12aSDW`\0\x80\xFD[\x815\x81\x81\x11\x15aSVWaSVaO\xCCV[aSh`\x1F\x82\x01`\x1F\x19\x16\x85\x01aP\x0BV[\x91P\x80\x82R\x87\x84\x82\x85\x01\x01\x11\x15aS~W`\0\x80\xFD[\x80\x84\x84\x01\x85\x84\x017`\0\x84\x82\x84\x01\x01RP\x80\x93PPPP\x92P\x92\x90PV[`\x01`\x01`@\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[\x805c\xFF\xFF\xFF\xFF\x81\x16\x81\x14a0\x94W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aS\xD6W`\0\x80\xFD[a \xAB\x82aS\xB0V[`\0` \x82\x84\x03\x12\x15aS\xF1W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`\0a\x01@\x84\x86\x03\x12\x15aT\x0EW`\0\x80\xFD[aT\x18\x85\x85aPRV[\x92PaT'a\x01\0\x85\x01aS\xB0V[\x91PaT6a\x01 \x85\x01aO\x86V[\x90P\x92P\x92P\x92V[`\0a\x01\0\x82\x84\x03\x12\x15aTRW`\0\x80\xFD[a \xAB\x83\x83aPRV[`\0[\x83\x81\x10\x15aTwW\x81\x81\x01Q\x83\x82\x01R` \x01aT_V[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01RaT\x9F\x81`@\x85\x01` \x87\x01aT\\V[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15aT\xC6W`\0\x80\xFD[PP\x805\x92` \x90\x91\x015\x91PV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01`\x01`@\x1B\x03\x81\x81\x16\x83\x82\x16\x02\x80\x82\x16\x91\x90\x82\x81\x14aA\xE9WaA\xE9aT\xD5V[\x81\x81\x03\x81\x81\x11\x15a\x10\x1AWa\x10\x1AaT\xD5V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81aUFWaUFaT\xD5V[P`\0\x19\x01\x90V[`\x01`\x01`@\x1B\x03\x81\x81\x16\x83\x82\x16\x01\x90\x80\x82\x11\x15a2\x15Wa2\x15aT\xD5V[`\0` \x82\x84\x03\x12\x15aU\x80W`\0\x80\xFD[PQ\x91\x90PV[\x80\x82\x01\x80\x82\x11\x15a\x10\x1AWa\x10\x1AaT\xD5V[`\0\x82QaU\xAC\x81\x84` \x87\x01aT\\V[\x91\x90\x91\x01\x92\x91PPV[`\0\x87Q` aU\xC9\x82\x85\x83\x8D\x01aT\\V[\x88Q\x91\x84\x01\x91aU\xDC\x81\x84\x84\x8D\x01aT\\V[\x88Q\x92\x01\x91aU\xEE\x81\x84\x84\x8C\x01aT\\V[\x87Q\x92\x01\x91aV\0\x81\x84\x84\x8B\x01aT\\V[\x86Q\x92\x01\x91aV\x12\x81\x84\x84\x8A\x01aT\\V[\x85Q\x92\x01\x91aV$\x81\x84\x84\x89\x01aT\\V[\x91\x90\x91\x01\x99\x98PPPPPPPPPV[`\0\x83QaVG\x81\x84` \x88\x01aT\\V[\x83Q\x90\x83\x01\x90aV[\x81\x83` \x88\x01aT\\V[\x01\x94\x93PPPPV[`\0\x87QaVv\x81\x84` \x8C\x01aT\\V[\x91\x90\x91\x01\x95\x86RP` \x85\x01\x93\x90\x93R`@\x84\x01\x91\x90\x91R``\x83\x01R`\x80\x82\x01R`\xA0\x01\x91\x90PV[`\0\x84QaV\xB2\x81\x84` \x89\x01aT\\V[\x84Q\x90\x83\x01\x90aV\xC6\x81\x83` \x89\x01aT\\V[\x84Q\x91\x01\x90aV\xD9\x81\x83` \x88\x01aT\\V[\x01\x95\x94PPPPPV[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82aW\x16WcNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[P\x06\x90V[`\0\x85QaW-\x81\x84` \x8A\x01aT\\V[\x85Q\x90\x83\x01\x90aWA\x81\x83` \x8A\x01aT\\V[\x85Q\x91\x01\x90aWT\x81\x83` \x89\x01aT\\V[\x84Q\x91\x01\x90aWg\x81\x83` \x88\x01aT\\V[\x01\x96\x95PPPPPPV[`\0\x89Q` aW\x85\x82\x85\x83\x8F\x01aT\\V[\x8AQ\x91\x84\x01\x91aW\x98\x81\x84\x84\x8F\x01aT\\V[\x8AQ\x92\x01\x91aW\xAA\x81\x84\x84\x8E\x01aT\\V[\x89Q\x92\x01\x91aW\xBC\x81\x84\x84\x8D\x01aT\\V[\x88Q\x92\x01\x91aW\xCE\x81\x84\x84\x8C\x01aT\\V[\x87Q\x92\x01\x91aW\xE0\x81\x84\x84\x8B\x01aT\\V[\x86Q\x92\x01\x91aW\xF2\x81\x84\x84\x8A\x01aT\\V[\x85Q\x92\x01\x91aX\x04\x81\x84\x84\x89\x01aT\\V[\x91\x90\x91\x01\x9B\x9APPPPPPPPPPPV[`\0\x88Q` aX*\x82\x85\x83\x8E\x01aT\\V[\x89Q\x91\x84\x01\x91aX=\x81\x84\x84\x8E\x01aT\\V[\x89Q\x92\x01\x91aXO\x81\x84\x84\x8D\x01aT\\V[\x88Q\x92\x01\x91aXa\x81\x84\x84\x8C\x01aT\\V[\x87Q\x92\x01\x91aXs\x81\x84\x84\x8B\x01aT\\V[\x86Q\x92\x01\x91aX\x85\x81\x84\x84\x8A\x01aT\\V[\x85Q\x92\x01\x91aX\x97\x81\x84\x84\x89\x01aT\\V[\x91\x90\x91\x01\x9A\x99PPPPPPPPPPV[`\0\x8AQaX\xBB\x81\x84` \x8F\x01aT\\V[\x91\x90\x91\x01\x98\x89RP` \x88\x01\x96\x90\x96R`@\x87\x01\x94\x90\x94R``\x86\x01\x92\x90\x92R`\x80\x85\x01R`\xA0\x84\x01R`\xC0\x83\x01R`\xE0\x82\x01Ra\x01\0\x01\x91\x90PV\xFE0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\xA6l\xC9(\xB5\xED\xB8*\xF9\xBDI\x92)T\x15Z\xB7\xB0\x94&\x94\xBE\xA4\xCEDf\x1D\x9A\x876\xC6\x88.+\x91Ea\x03i\x8A\xDFW\xB7\x99\x96\x9D\xEA\x1C\x8Fs\x9D\xA5\xD8\xD4\r\xD3\xEB\x92\"\xDB|\x81\xE8\x816\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE3\x1E\xE6x\xA0G\nu\xA6\xEA\xA8\xFE\x83p`I\x8B\xA8(\xA3p;1\x1D\x0Fw\xF0\x10BJ\xFE\xB0%\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE4 B\xA5\x87\xA9\x0C\x18{\n\x08|\x03\xE2\x9C\x96\x8B\x95\x0B\x1D\xB2m\\\x82\xD6f\x90Zh\x95y\x0C\n/\x8D\xD1\xF1\xA7X v\xCC75\xA9 \xA3\xCAP]8+\xBC\x90V[a\x0B\xCDa\x0FeV[`\x06Tt\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15a\x0CCW`\x06\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90U`@Q\x7F\x9A_W\xDE\x85m\xD6h\xC5M\xD9^\\U\xDF\x93C!q\xCB\xCAI\xA8wmV \xEAY\xC0$P\x90`\0\x90\xA1V[`@Q\x7F\xA8c\xAE\xC9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[V[a\x0C\x7Fa\x0FeV[a\x0Cu`\0a\x16>V[a\x0C\xE5`@Q\x80a\x01\0\x01`@R\x80`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[P`\0\x80Tc\xFF\xFF\xFF\xFFh\x01\0\0\0\0\0\0\0\0\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x84Qa\x01\0\x81\x01\x86R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R\x95\x90\x04\x90\x94\x16\x91\x84\x01\x91\x90\x91R`\x01\x81\x01T\x93\x83\x01\x93\x90\x93R`\x02\x83\x01T``\x83\x01R`\x03\x83\x01T`\x80\x83\x01R`\x04\x83\x01T`\xA0\x83\x01R\x82\x01T`\xC0\x82\x01R`\x06\x90\x91\x01T`\xE0\x82\x01R\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x81\x04`\xFF\x16\x15\x90g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\0\x81\x15\x80\x15a\r\xBEWP\x82[\x90P`\0\x82g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\x01\x14\x80\x15a\r\xDBWP0;\x15[\x90P\x81\x15\x80\x15a\r\xE9WP\x80\x15[\x15a\x0E W`@Q\x7F\xF9.\xE8\xA9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\x16`\x01\x17\x85U\x83\x15a\x0EkW\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16h\x01\0\0\0\0\0\0\0\0\x17\x85U[a\x0Et\x86a\x16\xC7V[a\x0E|a\x16\xD8V[`\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\xFF\xFF\xFF\xFF\x16h\x01\0\0\0\0\0\0\0\0\x17\x90Ua\x0E\xB9\x88\x88a\x16\xE0V[\x83\x15a\x0F\x04W\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16\x85U`@Q`\x01\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPPPV[a\x0F\x16a\x0FeV[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x0FYW`@Q\x7F\x1EO\xBD\xF7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\0`\x04\x82\x01R`$\x01a\tBV[a\x0Fb\x81a\x16>V[PV[3a\x0F\x97\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\x0CuW`@Q\x7F\x11\x8C\xDA\xA7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R3`\x04\x82\x01R`$\x01a\tBV[\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x81\x10\x80a\x0B\x92W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1B`$\x82\x01R\x7FBn254: invalid scalar field\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[`\0\x80Th\x01\0\0\0\0\0\0\0\0\x80\x82\x04c\xFF\xFF\xFF\xFF\x16\x80\x84R`\x05` \x81\x81R`@\x80\x87 \x81Qa\x01\0\x81\x01\x83R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R\x97\x90\x04\x87\x16\x81\x85\x01R`\x01\x80\x83\x01T\x82\x85\x01R`\x02\x80\x84\x01T``\x80\x85\x01\x91\x90\x91R`\x03\x80\x86\x01T`\x80\x80\x87\x01\x82\x90R`\x04\x80\x89\x01T`\xA0\x89\x01\x81\x90R\x89\x8D\x01T`\xC0\x8A\x01\x81\x90R`\x06\x90\x9A\x01\x80T`\xE0\x90\x9A\x01\x99\x90\x99R\x8AQ\x80\x8D\x01\x94\x90\x94R\x83\x8B\x01R\x82\x85\x01\x98\x90\x98R\x88Q\x80\x83\x03\x90\x94\x01\x84R\x01\x90\x96R\x80Q\x90\x87\x01 \x85T\x83U\x94\x85\x90U\x83T\x90U\x95\x89R\x93\x90\x92R\x91T\x90U\x93\x90\x92\x90\x91`\x0C\x91a\x11C\x91\x85\x91l\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04\x16aY\xBAV[\x82Ta\x01\0\x92\x90\x92\ng\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x81\x02\x19\x90\x93\x16\x91\x83\x16\x02\x17\x90\x91U`\0T`@Ql\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x91\x04\x90\x91\x16\x81R\x7F\xDB5X%\x9E\x03\x9D~P\xE8\x16\xB9\xDC\xCE0\xFB\x11M\x8A\x9C\x86\xEC\xA5\xAB\x14\xB6\x01\x94\xD6\x94]?\x91P` \x01a\x07=V[`\0a\x11\xB5a\x19\xD0V[`@\x80Q`\x08\x80\x82Ra\x01 \x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x01\0\x806\x837\x01\x90PP\x90P`\x02T\x81`\0\x81Q\x81\x10a\x11\xF4Wa\x11\xF4aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x83`\0\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81`\x01\x81Q\x81\x10a\x12\"Wa\x12\"aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x83` \x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81`\x02\x81Q\x81\x10a\x12PWa\x12PaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x83`@\x01Q\x81`\x03\x81Q\x81\x10a\x12tWa\x12taY\xDBV[` \x02` \x01\x01\x81\x81RPP\x83``\x01Q\x81`\x04\x81Q\x81\x10a\x12\x98Wa\x12\x98aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 `\x03\x01T\x82Q\x90\x91\x83\x91\x81\x10a\x12\xE1Wa\x12\xE1aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x90\x91R`@\x90 `\x04\x01T\x81Q\x82\x90`\x06\x90\x81\x10a\x13*Wa\x13*aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 \x01T\x81Q\x82\x90`\x07\x90\x81\x10a\x13rWa\x13raY\xDBV[` \x02` \x01\x01\x81\x81RPPa\x13\x89\x82\x82\x85a\x1F\xB2V[a\x13\xBFW`@Q\x7F\t\xBD\xE39\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[PPPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14\x80a\x14^WP\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16a\x14R\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBCT`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14\x15[\x15a\x0CuW`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x14\x9Da\x0FeV[`@Q`\x01`\x01`\xA0\x1B\x03\x82\x16\x81R\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x90` \x01a\x07=V[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x150WP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x15-\x91\x81\x01\x90aY\xF1V[`\x01[a\x15qW`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16`\x04\x82\x01R`$\x01a\tBV[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x81\x14a\x15\xCDW`@Q\x7F\xAA\x1DI\xA4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x81\x01\x82\x90R`$\x01a\tBV[a\x15\xD7\x83\x83a \x9DV[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\x0CuW`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x16`\x01`\x01`\xA0\x1B\x03\x84\x81\x16\x91\x82\x17\x84U`@Q\x92\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[a\x16\xCFa \xF3V[a\x0Fb\x81a!ZV[a\x0Cua \xF3V[\x81Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x15\x15\x80a\x17\x06WP` \x82\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x15\x15[\x80a\x17\x13WP`\x80\x82\x01Q\x15[\x80a\x17 WP`\xA0\x82\x01Q\x15[\x80a\x17-WP`\xC0\x82\x01Q\x15[\x80a\x17:WP`\xE0\x82\x01Q\x15[\x80a\x17IWPc\xFF\xFF\xFF\xFF\x81\x16\x15[\x15a\x17\x80W`@Q\x7F\xA1\xBA\x07\xEE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x81`\x05`\0\x80`\x04\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP\x81`\x05`\0\x80`\x08\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP`\0\x80`\x0Ca\x01\0\n\x81T\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP\x80`\0\x80a\x01\0\n\x81T\x81c\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83c\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`\0a\x19\xB3\x83`\x80\x80\x82\x01Q`\xA0\x83\x01Q`\xC0\x84\x01Q`@\x80Q` \x81\x01\x94\x90\x94R\x83\x01\x91\x90\x91R``\x82\x01R`\0\x91\x01`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[`\x01\x81\x90U`\xE0\x90\x93\x01Q`\x02\x81\x90U`\x03\x93\x90\x93UPP`\x04UV[a\x19\xD8aQcV[b\x10\0\0\x81R`\x08` \x82\x01R\x7F \xC9@13\xDF\xDE\x9A\x9D8-\xF7o\xB0R5qd\x87%\xAB\xC0\xA7\xC1(0\xBBi\x0E\xC8;3`@\x82\x01QR\x7F\x03\xA0\xA9\xAC\xC3\xE3\x81Z~\xD6\xCB\x13y\xF7\xD1W\xE641dr\x93v9*i:\xCB\xD3\xEC(<` `@\x83\x01Q\x01R\x7F(f\xC1\x8A\xD1\xDF\x10\xEF\x13T,\xCEbP\xCE\x02\xCB*kr\xAE\0\xA9\x85.'\x11\x87\xE9\xE4\xE0\xDB``\x82\x01QR\x7F!\xBE#*B$jVc\xEB\xF4\x83G\x0C\xCAfo\xFE\x9DO\x0Ec\xB9)\xC5\x96\xA7e\x87\x14\xE9p` ``\x83\x01Q\x01R\x7F\x07\xD7xs\xB9\x86\0t\x11\x8Eu\x80\x8CyF\x8B\x83\xC8\xEDd\xBA\x14\xDB\\\xB5\xAF\xA8\xE54\xDE{\x99`\x80\x82\x01QR\x7F\x0B\xE0\xF4H\x83\x90\x80\x13-G\xDE\x17\xDE\0\x99\xB4\xCDt\xAE\x1Ekq\xCD\xDA\x06\xCD\xEB\xB8h\xA5\x0Cm` `\x80\x83\x01Q\x01R\x7F\x13\xBDE\xA0#I\x1E\xAD\xEAD\xCC?$\xCF\xBD\x17\x96\xEA\xDE\x9C\x0E9\xEE\x81\xD9\xF6>\xA0\xA5\x80f%`\xA0\x82\x01QR\x7F\x18\xF9\\\xDD\xA4,\xE1\x1D\x9D\x10\xA3\xB35\xAC\xC2\x14\xE3\x80|W\x8CSY@]\x81\x0C \x8D\xF6\0\x93` `\xA0\x83\x01Q\x01R\x7F\tp\xD9xv4a\xF0\x9E\x9E\xC64T\x074\x978nM(/\xED\xC2\xAC[\x96|\xB9\xFD?\xA8\xA9`\xC0\x82\x01QR\x7F(\xC2!\x7F{\xAC\xF6\xF8\xB2\xB8\xEEJ\x90\xFC\xF8\xB5\xBC\xA0B\x05\xEA\x84\xE8\xE1\xEBT\xB8]\xD4\x1B\xDE(` `\xC0\x83\x01Q\x01R\x7F\x02\xFE=\x02\x98\x8D\xB7\x188\0R\x97\n\xBAF\xA3)m\xF5\xF2\x9Bsk\xA1\xF2\xC4\xCC\xFF\xC8\xB5\x96\x93`\xE0\x82\x01QR\x7F ,>9\x0C\xEE|\\\x85%\xDA#)\xA1\x9FI6\xF6\xF7\x1C\xA9}\xDElo\xA3+8-Z\xCC\x03` `\xE0\x83\x01Q\x01R\x7F#\xAC\x10\xAEl\xA5\xCA\xCE\xE8tK\xB99\xAA\xA859\tT\xB9\x1A\xE6h\xA2\xC8\xD0\xED\xDAU\x8A\x89\xE7a\x01\0\x82\x01QR\x7F\x1C\x8C+\x85l\xDA\xDE%k\xA3#\x7F9\xAF\xD5\xE1p\xA9S \x12\xF7\xAE\xCA\xE4\x9DE\x9B)\xF6\xF6\xAD` a\x01\0\x83\x01Q\x01R\x7F\x16\xEC\x03\xD2`\xBDz\xC1\xC5\x0F\xFAcV]Rt\xB4X,\xEE\xA5/\xF4\x0B\x81\xCD\xFE\x8FDO\x01\xE4a\x01 \x82\x01QR\x7F)9!Rr0\x97\xE0q\x13\xC3\xD7xm$^\xC4\x0C0\x92\x80\x15\xCDP\xB5f\x8AON\xA1p1` a\x01 \x83\x01Q\x01R\x7F,\xDB\xFB:@S\xC8H\x9B\x0C\x94\xE7C8\xAC\x19\x11\x8D\xF7\xA0k\xC5k\x1E\xB4\xD0\xE0\xDCN\xAErHa\x01@\x82\x01QR\x7F\x07\xFE\xA1'\xDA\xE9C\xB8\xDC\x14\x8F\x14\x08\xD4\x0C\xFFF\\\x9CG!\x946i\xB1\xE4\xFDZ9\xDBp6` a\x01@\x83\x01Q\x01R\x7F\x03\x14U\xA7\x9A.\x0C\xE7\x8Al\xB55&\xEC\x04\xAC\x19qj\x86\xB0\x8A\x93\xDFH\xD1x\xF8\xB7~V\x19a\x01`\x82\x01QR\x7F\x11\x86#\xE6\xBC\x13n\xE6\xD3\xF9\x90|\xD4\xAD\x04\xA9A\x8E\xA0;\xA9\x9A\xD7S\"|\xDF\xEEY\x8E\x84\x15` a\x01`\x83\x01Q\x01R\x7F\x08a\xD1\x99wa\xA8R\"j\xAC{\xA9q{\xF6\xAEVE\x10\x99\xBEwL\xDF\x02\xEF5*X\xCB\xC8a\x01\x80\x82\x01QR\x7F\x08\x05\xE3\x92\xBC\xBC\x12\xE4\nr'xc-s\xFE\x98\x1EK\xC6\xFAm\x11x\xB7\n\xF7\xBE\x1C\xB9\xA3\xA3` a\x01\x80\x83\x01Q\x01R\x7F\x10\x1D\x1E9x\xCB\x9F\x1E0=A1D\xEB\xE6v\x82\xC9\xEB\x0C\xFE\x11$)Y\xAA`)\xD7\x8C\xDB\xBCa\x01\xA0\x82\x01QR\x7F\x08\x9E\xB9\xC7'\xE6\xCB\x07\x08+\xC3\xE6\xF4\x0C\xF0OC\x9F\xE4\x80\0`+XGt\xDA\xD7\xEF\xC6`|` a\x01\xA0\x83\x01Q\x01R\x7F-H\x9F$\x93&:\xA8s\xBC\xD9O!\xEF\xB4[\xF2W\xA6\x1D\x81\xC0\xC9\\2\x97\x91e\x06e;@a\x01\xC0\x82\x01QR\x7F\x18\xE4]bz\xAD\xD4\xDF'\x94\xEC\xD9\x90\x9F\xAC\x1Au?\x0Co\xA8\xA9\xC6eJzX\xB0\x91/\xFF\xD5` a\x01\xC0\x83\x01Q\x01R\x7F\x0EC\xE3\xA4\xB1<\xB48\xE2\xAD\x92F\x14&\x1A\xD0$\x02\x14\xFA\x1C\x83\xFC\xDAj\x0B\xF7y\xEB9\xFF\xC5a\x01\xE0\x82\x01QR\x7F\x0E\xAB\xA9\xF4)\xC5\xF6\xFC1\x03\xD4\xCC@V\xC5\0\xFFBB]\x8Ede\xC5\xB8\xE1E!\x9F\x9C\\\xD3` a\x01\xE0\x83\x01Q\x01R\x7F)\xAE5\x1D\t\xDC\xF4\x1C\n\x80\xAB\x059785\x8B\xAA\xB3~o\xBCFK;\xB12X\x99J\x1F\xA4a\x02\0\x82\x01QR\x7F+{\xC7F\x08\xD7\xEC}\xAD\xD0Y}j@\x10\xD8\xBF\xC2\xB3\x19\0(\x19\x01\xCE\xDCB\xBD\xBB\x0F\xB8\xFC` a\x02\0\x83\x01Q\x01R\x7F\x06h\x02\xC7\xCE\xB9\xE9\x13\xD4\xF6T3\xA2\x06a\xE0\x97\xAC\xAC\x1A\xFF\xEC\xBBSJT\xF7j)x\"&a\x02 \x82\x01QR\x7F'\xEC\x80\xE8\x11\xE66\xF34\x82g\x92<\x8Ed\x1B\xD9\x8A~7\xC5!fp\xCB\xFF\x14\xAE2?\x9E\x0E` a\x02 \x83\x01Q\x01R\x7F\x12`M\x1F\x87\xC5\x83\xF6\xC9q\x0Cs\xEA\xF5\x90\xAF\x9D\x07\xAAt=\x13\x81\xD0\xE9\xDF\xF0\xEA\xB2aB9a\x02@\x82\x01QR\x7F\x15\x88W\x9El3x\xEA2\xCBd\x12\x05\xEFv*c\xCD5:\x0B\xD6p9E(\xAD \x81\xEE\x8D\xD4` a\x02@\x83\x01Q\x01R\x7F$}e&\x1D:J\xB0B\xBA\x93s1\xF6\xD0\xC0\xC5\xEB\x9E\xA7\x87S\xA9 \x84\xDB\x1Ai9\xE1\x9E\x82a\x02`\x82\x01QR\x7F,\xE6\xCCfJ2\x14{\xFEj\x0C\x94\xA9[\xF0Ify@\\\xCA\xE0\x16H\xCDN\xC0!\x14Q \xD5` a\x02`\x83\x01Q\x01R\x90V[`\0a\x1F\xBD\x82a!bV[a\x1F\xE0\x83`\0\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[` \x02` \x01\x01Qa\x0F\xD9V[a\x1F\xF6\x83`\x01\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a \x0C\x83`\x02\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a \"\x83`\x03\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a 8\x83`\x04\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a N\x83`\x05\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a d\x83`\x06\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[a z\x83`\x07\x81Q\x81\x10a\x1F\xD3Wa\x1F\xD3aY\xDBV[`\0a \x87\x85\x85\x85a\"\x9AV[\x90Pa \x92\x81a$\x1BV[\x91PP[\x93\x92PPPV[a \xA6\x82a(\xE3V[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a \xEBWa\x15\xD7\x82\x82a)\x8BV[a\x0B\x92a*\x03V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16a\x0CuW`@Q\x7F\xD7\xE6\xBC\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x0F\x16a \xF3V[\x80Qa!m\x90a*;V[a!z\x81` \x01Qa*;V[a!\x87\x81`@\x01Qa*;V[a!\x94\x81``\x01Qa*;V[a!\xA1\x81`\x80\x01Qa*;V[a!\xAE\x81`\xA0\x01Qa*;V[a!\xBB\x81`\xC0\x01Qa*;V[a!\xC8\x81`\xE0\x01Qa*;V[a!\xD6\x81a\x01\0\x01Qa*;V[a!\xE4\x81a\x01 \x01Qa*;V[a!\xF2\x81a\x01@\x01Qa*;V[a\"\0\x81a\x01`\x01Qa*;V[a\"\x0E\x81a\x01\x80\x01Qa*;V[a\"\x1C\x81a\x01\xA0\x01Qa\x0F\xD9V[a\"*\x81a\x01\xC0\x01Qa\x0F\xD9V[a\"8\x81a\x01\xE0\x01Qa\x0F\xD9V[a\"F\x81a\x02\0\x01Qa\x0F\xD9V[a\"T\x81a\x02 \x01Qa\x0F\xD9V[a\"b\x81a\x02@\x01Qa\x0F\xD9V[a\"p\x81a\x02`\x01Qa\x0F\xD9V[a\"~\x81a\x02\x80\x01Qa\x0F\xD9V[a\"\x8C\x81a\x02\xA0\x01Qa\x0F\xD9V[a\x0Fb\x81a\x02\xC0\x01Qa\x0F\xD9V[a\"\xA2aS\xE1V[\x83` \x01Q\x83Q\x14a\"\xE0W`@Q\x7FA\xF5;\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\"\xED\x85\x85\x85a*\xE5V[\x90P`\0a\"\xFE\x86`\0\x01Qa.\x16V[\x90P`\0a#\x11\x82\x84`\xA0\x01Q\x88a1\xFAV[`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x03\xC0\x806\x837PP`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a#TW\x90PP\x90P`\0a#\x8D\x8A\x85\x8A\x89\x87\x87a2ZV[`\xA0\x87\x01Q``\x87\x01Q\x91\x92P\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01`\0\x81\x83\x85\t`@\x80Qa\x01\0\x81\x01\x82R`\xE0\x9C\x8D\x01Q\x81R` \x81\x01\x96\x90\x96R\x85\x01RPPP``\x81\x01\x91\x90\x91R`\x80\x81\x01\x92\x90\x92R`\xA0\x82\x01Ra\x01`\x86\x01Q`\xC0\x82\x01Ra\x01\x80\x90\x95\x01Q\x92\x85\x01\x92\x90\x92RP\x91\x94\x93PPPPV[`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x80\x83\x01\x82\x90R\x83Q\x80\x85\x01\x85R\x82\x81R\x90\x81\x01\x82\x90R\x83Q`\x02\x80\x82R``\x82\x01\x90\x95R\x91\x93\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x93\x92\x85\x91\x81` \x01` \x82\x02\x806\x837PP`@\x80Q`\x02\x80\x82R``\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a$\xA1W\x90PP\x90P`\0`\x01\x90P\x80\x83`\0\x81Q\x81\x10a$\xE4Wa$\xE4aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x87`\xC0\x01Q\x82`\0\x81Q\x81\x10a%\x08Wa%\x08aY\xDBV[` \x02` \x01\x01\x81\x90RP\x87`\0\x01Q\x83`\x01\x81Q\x81\x10a%+Wa%+aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x87`\xE0\x01Q\x82`\x01\x81Q\x81\x10a%OWa%OaY\xDBV[` \x02` \x01\x01\x81\x90RPa%d\x82\x84a2\x8FV[`\x80\x89\x01QQ\x90\x95P``\x93P\x83\x92P\x90P`\0a%\x83\x82`\x02aZ\nV[a%\x8E\x90`\x01aZ\nV[\x90P\x80g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a%\xA9Wa%\xA9aT\xA2V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a%\xD2W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x80g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a%\xEEWa%\xEEaT\xA2V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a&3W\x81` \x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a&\x0CW\x90P[P\x92PPP`\0\x80`\0[\x89`\x80\x01QQ\x81\x10\x15a&\xD7W\x89`\x80\x01Q\x81\x81Q\x81\x10a&aWa&aaY\xDBV[` \x02` \x01\x01Q\x85\x83\x81Q\x81\x10a&{Wa&{aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x89`\xA0\x01Q\x81\x81Q\x81\x10a&\x9DWa&\x9DaY\xDBV[` \x02` \x01\x01Q\x84\x83\x81Q\x81\x10a&\xB7Wa&\xB7aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x01Ra&\xCD`\x01\x83aZ\nV[\x91P`\x01\x01a&>V[P\x88` \x01Q\x84\x82\x81Q\x81\x10a&\xEFWa&\xEFaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88`\xC0\x01Q\x83\x82\x81Q\x81\x10a'\x12Wa'\x12aY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x01Ra'(`\x01\x82aZ\nV[\x89Q`@\x8B\x01Q\x91\x92P\x90`\0\x89\x82\x84\t\x90P\x80\x87\x85\x81Q\x81\x10a'NWa'NaY\xDBV[` \x02` \x01\x01\x81\x81RPPPPP\x88`\xE0\x01Q\x83\x82\x81Q\x81\x10a'tWa'taY\xDBV[` \x90\x81\x02\x91\x90\x91\x01\x01Ra'\x8A`\x01\x82aZ\nV[``\x8A\x01Q\x90\x91P\x87\x81\x84\x08\x92PPa'\xA2\x82a3\x89V[\x84\x82\x81Q\x81\x10a'\xB4Wa'\xB4aY\xDBV[` \x02` \x01\x01\x81\x81RPPa'\xEC`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x91\x82\x01R\x81Q\x80\x83\x01\x90\x92R`\x01\x82R`\x02\x90\x82\x01R\x90V[\x83\x82\x81Q\x81\x10a'\xFEWa'\xFEaY\xDBV[` \x02` \x01\x01\x81\x90RPa(\x1Ba(\x16\x84\x86a2\x8FV[a3\xDFV[\x94PPPPP`\0`@Q\x80`\x80\x01`@R\x80\x7F\x01\x18\xC4\xD5\xB87\xBC\xC2\xBC\x89\xB5\xB3\x98\xB5\x97N\x9FYD\x07;2\x07\x8B~#\x1F\xEC\x93\x88\x83\xB0\x81R` \x01\x7F&\x0E\x01\xB2Q\xF6\xF1\xC7\xE7\xFFNX\x07\x91\xDE\xE8\xEAQ\xD8z5\x8E\x03\x8BN\xFE0\xFA\xC0\x93\x83\xC1\x81R` \x01\x7F\"\xFE\xBD\xA3\xC0\xC0c*VG[B\x14\xE5a^\x11\xE6\xDD?\x96\xE6\xCE\xA2\x85J\x87\xD4\xDA\xCC^U\x81R` \x01\x7F\x04\xFCci\xF7\x11\x0F\xE3\xD2QV\xC1\xBB\x9Ar\x85\x9C\xF2\xA0FA\xF9\x9B\xA4\xEEA<\x80\xDAj_\xE4\x81RP\x90Pa(\xD9\x83\x82\x84a(\xD4a4~V[a5OV[\x96\x95PPPPPPV[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a)2W`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16`\x04\x82\x01R`$\x01a\tBV[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa)\xA8\x91\x90aZ\x1DV[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a)\xE3W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a)\xE8V[``\x91P[P\x91P\x91Pa)\xF8\x85\x83\x83a63V[\x92PPP[\x92\x91PPV[4\x15a\x0CuW`@Q\x7F\xB3\x98\x97\x9F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80Q` \x82\x01Q`\0\x91\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\x91\x15\x90\x15\x16\x15a*uWPPPV[\x82Q` \x84\x01Q\x82`\x03\x84\x85\x85\x86\t\x85\t\x08\x83\x82\x83\t\x14\x83\x82\x10\x84\x84\x10\x16\x16\x93PPP\x81a\x15\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x17`$\x82\x01R\x7FBn254: invalid G1 point\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[a+-`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@\x80Q\x80\x82\x01\x90\x91R``\x81R`\0` \x82\x01R\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01a+n\x82\x87\x87a6\xA8V[\x81Q\x84Qa+{\x90a:\x89V[a+\x88\x86` \x01Qa:\x89V[a+\x95\x87`@\x01Qa:\x89V[a+\xA2\x88``\x01Qa:\x89V[a+\xAF\x89`\x80\x01Qa:\x89V[`@Q` \x01a+\xC4\x96\x95\x94\x93\x92\x91\x90aZ9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra+\xDF\x82a;LV[Pa+\xE9\x82a;LV[``\x84\x01Ra+\xF7\x82a;LV[`\x80\x84\x01R\x81Q`\xA0\x85\x01Qa,\x0C\x90a:\x89V[`@Q` \x01a,\x1D\x92\x91\x90aZ\xB8V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,8\x82a;LV[\x83R\x81Q`\xC0\x85\x01Qa,J\x90a:\x89V[a,W\x86`\xE0\x01Qa:\x89V[a,e\x87a\x01\0\x01Qa:\x89V[a,s\x88a\x01 \x01Qa:\x89V[a,\x81\x89a\x01@\x01Qa:\x89V[`@Q` \x01a,\x96\x96\x95\x94\x93\x92\x91\x90aZ9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\xB1\x82a;LV[`\xA0\x84\x01R\x81Qa\x01\xA0\x85\x01Qa,\xC7\x90a;\xC0V[a,\xD5\x86a\x01\xC0\x01Qa;\xC0V[a,\xE3\x87a\x01\xE0\x01Qa;\xC0V[a,\xF1\x88a\x02\0\x01Qa;\xC0V[a,\xFF\x89a\x02 \x01Qa;\xC0V[`@Q` \x01a-\x14\x96\x95\x94\x93\x92\x91\x90aZ\xE7V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x83Ra\x02@\x85\x01Qa-6\x90a;\xC0V[a-D\x86a\x02`\x01Qa;\xC0V[a-R\x87a\x02\x80\x01Qa;\xC0V[a-`\x88a\x02\xA0\x01Qa;\xC0V[a-n\x89a\x02\xC0\x01Qa;\xC0V[`@Q` \x01a-\x83\x96\x95\x94\x93\x92\x91\x90aZ\xE7V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra-\x9E\x82a;LV[`\xC0\x84\x01R\x81Qa\x01`\x85\x01Qa-\xB4\x90a:\x89V[a-\xC2\x86a\x01\x80\x01Qa:\x89V[`@Q` \x01a-\xD4\x93\x92\x91\x90a[#V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra-\xEF\x82a;LV[`\xE0\x84\x01R\x82Q\x81\x81\x80\t\x82\x82\x82\t` \x86\x01\x91\x90\x91R`@\x85\x01RP\x91\x95\x94PPPPPV[a.H`@Q\x80`\xA0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[\x81b\x01\0\0\x03a.\xDCWP`@\x80Q`\xA0\x81\x01\x82R`\x10\x81R` \x81\x01\x92\x90\x92R\x7F0d\x1E\x0E\x92\xBE\xBE\xF8\x18&\x8Df;\xCA\xD6\xDB\xCF\xD6\xC0\x14\x91p\xF6\xD7\xD3P\xB1\xB1\xFAl\x10\x01\x90\x82\x01R~\xEE\xB2\xCBY\x81\xEDEd\x9A\xBE\xBD\xE0\x81\xDC\xFF\x16\xC8`\x1D\xE44~}\xD1b\x8B\xA2\xDA\xACC\xB7``\x82\x01R\x7F\x0B]V\xB7\x7F\xE7\x04\xE8\xE9#8\xC0\x08/7\xE0\x91\x12d\x14\xC80\xE4\xC6\x92-Z\xC8\x02\xD8B\xD4`\x80\x82\x01R\x90V[\x81b\x02\0\0\x03a/qWP`@\x80Q`\xA0\x81\x01\x82R`\x11\x81R` \x81\x01\x92\x90\x92R\x7F0d6@\xB9\xF8/\x90\xE8;i\x8E^\xA6\x17\x9C|\x05T.\x85\x953\xB4\x8B\x99S\xA2\xF56\x08\x01\x90\x82\x01R\x7F\x1B\xF8-\xEB\xA7\xD7I\x02\xC3p\x8C\xC6\xE7\x0Ea\xF3\x05\x12\xEC\xA9VU!\x0E'nXX\xCE\x8FX\xE5``\x82\x01R\x7F$L\xF0\x10\xC4<\xA8r7\xD8\xB0\x0B\xF9\xDDP\xC4\xC0\x1C\x7F\x08k\xD4\xE8\xC9 \xE7RQ\xD9o\r\"`\x80\x82\x01R\x90V[\x81b\x04\0\0\x03a0\x06WP`@\x80Q`\xA0\x81\x01\x82R`\x12\x81R` \x81\x01\x92\x90\x92R\x7F0dBY\xCD\x94\xE7\xDDPE\xD7\xA2p\x13\xB7\xFC\xD2\x1C\x9E;\x7F\xA7R\"\xE7\xBD\xA4\x9Br\x9B\x04\x01\x90\x82\x01R\x7F\x19\xDD\xBC\xAF:\x8DF\xC1\\\x01v\xFB\xB5\xB9^M\xC5p\x88\xFF\x13\xF4\xD1\xBD\x84\xC6\xBF\xA5}\xCD\xC0\xE0``\x82\x01R\x7F\x03hS\xF0\x83x\x0E\x87\xF8\xD7\xC7\x1D\x11\x11\x19\xC5}\xBE\x11\x8C\"\xD5\xADpz\x821tf\xC5\x17L`\x80\x82\x01R\x90V[\x81b\x08\0\0\x03a0\x9BWP`@\x80Q`\xA0\x81\x01\x82R`\x13\x81R` \x81\x01\x92\x90\x92R\x7F0dHfWcD\x03\x84K\x0E\xACx\xCA\x88,\xFD(CA\xFC\xB0aZ\x15\xCF\xCD\x17\xB1M\x82\x01\x90\x82\x01R\x7F\"`\xE7$\x84K\xCARQ\x82\x93S\x96\x8EI\x150RXA\x83WG:\\\x1DY\x7Fa?l\xBD``\x82\x01R\x7F\x06\xE4\x02\xC0\xA3\x14\xFBg\xA1\\\xF8\x06fJ\xE1\xB7\"\xDB\xC0\xEF\xE6nl\x81\xD9\x8F\x99$\xCASS!`\x80\x82\x01R\x90V[\x81b\x10\0\0\x03a10WP`@\x80Q`\xA0\x81\x01\x82R`\x14\x81R` \x81\x01\x92\x90\x92R\x7F0dKl\x9CJr\x16\x9EM\xAA1}%\xF0E\x12\xAE\x15\xC5;4\xE8\xF5\xAC\xD8\xE1U\xD0\xA6\xC1\x01\x90\x82\x01R\x7F&\x12]\xA1\n\x0E\xD0c'P\x8A\xBA\x06\xD1\xE3\x03\xACaf2\xDB\xED4\x9FSB-\xA9S3xW``\x82\x01R\x7F\x10\x0C3-!\0\x89_\xABds\xBC,Q\xBF\xCAR\x1FE\xCB;\xAC\xA6&\x08R\xA8\xFD\xE2l\x91\xF3`\x80\x82\x01R\x90V[\x81` \x03a1\xC3WP`@\x80Q`\xA0\x81\x01\x82R`\x05\x81R` \x81\x01\x92\x90\x92R\x7F.\xE1+\xFFJ(\x13(j\x8D\xC3\x88\xCDuM\x9A>\xF2I\x065\xEB\xA5\x0C\xB9\xC2\xE5\xE7P\x80\0\x01\x90\x82\x01R\x7F\t\xC52\xC60k\x93\xD2\x96x \rG\xC0\xB2\xA9\x9C\x18\xD5\x1B\x83\x8E\xEB\x1D>\xEDLS;\xB5\x12\xD0``\x82\x01R\x7F'$q6\x03\xBF\xBDy\n\xEA\xF3\xE7\xDF%\xD8\xE7\xEF\x8F1\x134\x90[M\x8C\x99\x98\x0C\xF2\x10\x97\x9D`\x80\x82\x01R\x90V[`@Q\x7F\xE2\xEF\t\xE5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x91\x90PV[a2\x1E`@Q\x80``\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[a2(\x84\x84a=\x0CV[\x80\x82Ra28\x90\x85\x90\x85\x90a=rV[` \x82\x01R\x80Qa2N\x90\x85\x90\x84\x90\x86\x90a=\xF8V[`@\x82\x01R\x93\x92PPPV[`\0\x80a2h\x85\x87\x89a?\xBFV[\x90Pa2x\x88\x86\x89\x89\x88\x88a@\xBDV[a2\x83\x81\x87\x86aC\xDBV[\x98\x97PPPPPPPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x82Q\x82Q\x14a2\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FMSM error: length does not match`D\x82\x01R`d\x01a\tBV[a32\x83`\0\x81Q\x81\x10a3\nWa3\naY\xDBV[` \x02` \x01\x01Q\x83`\0\x81Q\x81\x10a3%Wa3%aY\xDBV[` \x02` \x01\x01QaD=V[\x90P`\x01[\x82Q\x81\x10\x15a3\x82Wa3x\x82a3s\x86\x84\x81Q\x81\x10a3YWa3YaY\xDBV[` \x02` \x01\x01Q\x86\x85\x81Q\x81\x10a3%Wa3%aY\xDBV[aD\xE1V[\x91P`\x01\x01a37V[P\x92\x91PPV[`\0a3\xB5\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x83a[|V[a)\xFD\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01a[\x9EV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81Q` \x83\x01Q\x15\x90\x15\x16\x15a4\x07WP\x90V[`@Q\x80`@\x01`@R\x80\x83`\0\x01Q\x81R` \x01\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\x84` \x01Qa4L\x91\x90a[|V[a4v\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDGa[\x9EV[\x90R\x92\x91PPV[a4\xA9`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80`\x80\x01`@R\x80\x7F\x18\0\xDE\xEF\x12\x1F\x1EvBj\0f^\\DygC\"\xD4\xF7^\xDA\xDDF\xDE\xBD\\\xD9\x92\xF6\xED\x81R` \x01\x7F\x19\x8E\x93\x93\x92\rH:r`\xBF\xB71\xFB]%\xF1\xAAI35\xA9\xE7\x12\x97\xE4\x85\xB7\xAE\xF3\x12\xC2\x81R` \x01\x7F\x12\xC8^\xA5\xDB\x8Cm\xEBJ\xABq\x80\x8D\xCB@\x8F\xE3\xD1\xE7i\x0CC\xD3{L\xE6\xCC\x01f\xFA}\xAA\x81R` \x01\x7F\t\x06\x89\xD0X_\xF0u\xEC\x9E\x99\xADi\x0C3\x95\xBCK13p\xB3\x8E\xF3U\xAC\xDA\xDC\xD1\"\x97[\x81RP\x90P\x90V[`\0\x80`\0`@Q\x87Q\x81R` \x88\x01Q` \x82\x01R` \x87\x01Q`@\x82\x01R\x86Q``\x82\x01R``\x87\x01Q`\x80\x82\x01R`@\x87\x01Q`\xA0\x82\x01R\x85Q`\xC0\x82\x01R` \x86\x01Q`\xE0\x82\x01R` \x85\x01Qa\x01\0\x82\x01R\x84Qa\x01 \x82\x01R``\x85\x01Qa\x01@\x82\x01R`@\x85\x01Qa\x01`\x82\x01R` `\0a\x01\x80\x83`\x08Z\xFA\x91PP`\0Q\x91P\x80a6%W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1C`$\x82\x01R\x7FBn254: Pairing check failed!\0\0\0\0`D\x82\x01R`d\x01a\tBV[P\x15\x15\x90P[\x94\x93PPPPV[``\x82a6HWa6C\x82aE\x88V[a \x96V[\x81Q\x15\x80\x15a6_WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a6\xA1W`@Q\x7F\x99\x96\xB3\x15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x85\x16`\x04\x82\x01R`$\x01a\tBV[P\x80a \x96V[\x82Q`\xFE\x90a6\xE3a6\xB9\x83a;\xC0V[`@Q` \x01a6\xCB\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x04aE\xCAV[a7\x1Da6\xF3\x86`\0\x01Qa;\xC0V[`@Q` \x01a7\x05\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x08aE\xCAV[a7-a6\xF3\x87` \x01Qa;\xC0V[`@Q` \x01a7@\x94\x93\x92\x91\x90a[\xB1V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85Ra7]`\x01a;\xC0V[a7\x86\x7F/\x8D\xD1\xF1\xA7X\x8BW`\x01`\0[\x82\x81\x10\x15a>~W\x81\x87\x03a>_W\x87\x81\x81Q\x81\x10a>LWa>LaY\xDBV[` \x02` \x01\x01Q\x94PPPPPa6+V[\x83\x80a>mWa>ma[fV[\x89``\x01Q\x83\t\x91P`\x01\x01a>+V[P`\0\x93PPPPa6+V[`\0\x80`\0\x80\x8A`@\x01Q\x90P`\0\x80a>\xA5\x8D\x88aG\xAAV[\x90P`\0\x87g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a>\xC2Wa>\xC2aT\xA2V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a>\xEBW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P\x88\x8B\x85\t\x93P`\x01\x92P`\0[\x88\x81\x10\x15a?0W` \x81\x02` \x84\x01\x01Q\x95P\x89\x8D\x87\x8C\x03\x08\x96P\x89\x87\x85\t` \x82\x81\x02\x84\x01\x01\x88\x90R\x93P`\x01\x01a>\xFBV[Pa?:\x83aF\xF2V[\x92P`\0[\x88\x81\x10\x15a?\xADW` \x81\x02` \x84\x01\x01Q\x95P\x89\x86\x86\t\x97P\x89\x84\x89\t\x97P`\0[\x89\x81\x10\x15a?\x8CW\x80\x82\x14a?\x84W` \x81\x02` \x84\x01\x01Q\x97P\x8A\x88\x8A\t\x98P[`\x01\x01a?bV[P` \x81\x02` \x8F\x01\x01Q\x95P\x89\x86\x89\t\x97P\x89\x88\x8C\x08\x9AP`\x01\x01a??V[PPPPPPPPPP\x94\x93PPPPV[`\0\x80\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P`\0\x83` \x01Q\x90P`\0\x84`@\x01Q\x90P`\0`\x01\x90P``\x88\x01Q`\x80\x89\x01Qa\x01\xA0\x89\x01Qa\x02@\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xC0\x89\x01Qa\x02`\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xE0\x89\x01Qa\x02\x80\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02\0\x89\x01Qa\x02\xA0\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02 \x89\x01Q\x91Pa\x02\xC0\x89\x01Q\x86\x87\x82\x89\x85\x87\x08\t\x85\t\x93PPPP\x87Q` \x89\x01Q\x85\x86\x86\x83\t\x87\x03\x85\x08\x96PP\x84\x85\x83\x83\t\x86\x03\x87\x08\x99\x98PPPPPPPPPV[a@\xCB\x86\x86\x86\x86\x85\x87aH\x9BV[`\xC0\x85\x01Q\x82Q\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x91\x90\x81\x90\x81\x90\x86\x90`\x14\x90\x81\x10aA\x0CWaA\x0CaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85`\0\x01Q\x84`\x14\x81Q\x81\x10aA0WaA0aY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x15\x81Q\x81\x10aAUWaAUaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85` \x01Q\x84`\x15\x81Q\x81\x10aAyWaAyaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x16\x81Q\x81\x10aA\x9EWaA\x9EaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85`@\x01Q\x84`\x16\x81Q\x81\x10aA\xC2WaA\xC2aY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x17\x81Q\x81\x10aA\xE7WaA\xE7aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85``\x01Q\x84`\x17\x81Q\x81\x10aB\x0BWaB\x0BaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x18\x81Q\x81\x10aB0WaB0aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85`\x80\x01Q\x84`\x18\x81Q\x81\x10aBTWaBTaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x19\x81Q\x81\x10aByWaByaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88`@\x01Q\x84`\x19\x81Q\x81\x10aB\x9DWaB\x9DaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1A\x81Q\x81\x10aB\xC2WaB\xC2aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88``\x01Q\x84`\x1A\x81Q\x81\x10aB\xE6WaB\xE6aY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1B\x81Q\x81\x10aC\x0BWaC\x0BaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88`\x80\x01Q\x84`\x1B\x81Q\x81\x10aC/WaC/aY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1C\x81Q\x81\x10aCTWaCTaY\xDBV[` \x02` \x01\x01\x81\x81RPP\x88`\xA0\x01Q\x84`\x1C\x81Q\x81\x10aCxWaCxaY\xDBV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x87`\xE0\x01Q\x85`\x1D\x81Q\x81\x10aC\xA1WaC\xA1aY\xDBV[` \x02` \x01\x01\x81\x81RPP\x85`\xA0\x01Q\x84`\x1D\x81Q\x81\x10aC\xC5WaC\xC5aY\xDBV[` \x02` \x01\x01\x81\x90RPPPPPPPPPPV[\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x83\x81\x03\x90`\0[`\n\x81\x10\x15aD4W` `\x15\x82\x01\x02\x84\x01Q` \x82\x02a\x01\xA0\x01\x86\x01Q\x83\x84\x82\x84\t\x86\x08\x94PPP`\x01\x01aD\x03V[PP\x93\x92PPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RaDYaT4V[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x81\x01\x83\x90R`\0``\x83`\x80\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80aD\x8BW`\0\x80\xFD[P\x80aD\xD9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01R\x7FBn254: scalar mul failed!\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[PP\x92\x91PPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RaD\xFDaTRV[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x83\x01R\x83\x01Q``\x80\x83\x01\x91\x90\x91R`\0\x90\x83`\xC0\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80aE:W`\0\x80\xFD[P\x80aD\xD9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: group addition failed!\0\0\0`D\x82\x01R`d\x01a\tBV[\x80Q\x15aE\x98W\x80Q\x80\x82` \x01\xFD[`@Q\x7F\x14%\xEAB\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x81aE\xD8\x81`\x1FaZ\nV[\x10\x15aF&W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x0E`$\x82\x01R\x7Fslice_overflow\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[aF0\x82\x84aZ\nV[\x84Q\x10\x15aF\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x11`$\x82\x01R\x7Fslice_outOfBounds\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\tBV[``\x82\x15\x80\x15aF\x9FW`@Q\x91P`\0\x82R` \x82\x01`@RaF\xE9V[`@Q\x91P`\x1F\x84\x16\x80\x15` \x02\x81\x84\x01\x01\x85\x81\x01\x87\x83\x15` \x02\x84\x8B\x01\x01\x01[\x81\x83\x10\x15aF\xD8W\x80Q\x83R` \x92\x83\x01\x92\x01aF\xC0V[PP\x85\x84R`\x1F\x01`\x1F\x19\x16`@RP[P\x94\x93PPPPV[`\0\x80`\0\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P`@Q` \x81R` \x80\x82\x01R` `@\x82\x01R\x84``\x82\x01R`\x02\x82\x03`\x80\x82\x01R\x81`\xA0\x82\x01R` `\0`\xC0\x83`\x05Z\xFA\x92PP`\0Q\x92P\x81aG\xA3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: pow precompile failed!\0\0\0`D\x82\x01R`d\x01a\tBV[PP\x91\x90PV[``\x82` \x01Q\x82\x11\x15aG\xEAW`@Q\x7F\x8C^\x11\xF1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x83\x01Q`\x01\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x84g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15aH+WaH+aT\xA2V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15aHTW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x84\x15\x19\x15a=iW` \x84\x01\x85` \x02\x81\x01`\x01\x82R` \x82\x01\x91P[\x80\x82\x10\x15aH\x90W\x82\x85\x85\t\x93P\x83\x82R` \x82\x01\x91PaHtV[PPPPP\x92\x91PPV[`\0\x80`\0\x80`\0\x80\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P\x80` \x8B\x01Q` \x8D\x01Q\t\x95P\x8AQ\x93P\x80`\xA0\x8C\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80\x7F/\x8D\xD1\xF1\xA7XW`\0\x80\xFD[\x815\x81\x81\x11\x15aXPWaXPaT\xA2V[aXb\x84`\x1F\x19`\x1F\x84\x01\x16\x01aT\xE2V[\x91P\x80\x82R\x87\x84\x82\x85\x01\x01\x11\x15aXxW`\0\x80\xFD[\x80\x84\x84\x01\x85\x84\x017`\0\x84\x82\x84\x01\x01RP\x80\x93PPPP\x92P\x92\x90PV[\x805c\xFF\xFF\xFF\xFF\x81\x16\x81\x14a1\xF5W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aX\xBCW`\0\x80\xFD[a \x96\x82aX\x96V[`\0\x80`\0a\x01@\x84\x86\x03\x12\x15aX\xDBW`\0\x80\xFD[aX\xE5\x85\x85aU+V[\x92PaX\xF4a\x01\0\x85\x01aX\x96V[\x91PaY\x03a\x01 \x85\x01aTpV[\x90P\x92P\x92P\x92V[`\0a\x01\0\x82\x84\x03\x12\x15aY\x1FW`\0\x80\xFD[a \x96\x83\x83aU+V[`\0[\x83\x81\x10\x15aYDW\x81\x81\x01Q\x83\x82\x01R` \x01aY,V[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01RaYl\x81`@\x85\x01` \x87\x01aY)V[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x81\x16\x83\x82\x16\x02\x80\x82\x16\x91\x90\x82\x81\x14aD\xD9WaD\xD9aY\x80V[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x81\x16\x83\x82\x16\x01\x90\x80\x82\x11\x15a3\x82Wa3\x82aY\x80V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0` \x82\x84\x03\x12\x15aZ\x03W`\0\x80\xFD[PQ\x91\x90PV[\x80\x82\x01\x80\x82\x11\x15a)\xFDWa)\xFDaY\x80V[`\0\x82QaZ/\x81\x84` \x87\x01aY)V[\x91\x90\x91\x01\x92\x91PPV[`\0\x87Q` aZL\x82\x85\x83\x8D\x01aY)V[\x88Q\x91\x84\x01\x91aZ_\x81\x84\x84\x8D\x01aY)V[\x88Q\x92\x01\x91aZq\x81\x84\x84\x8C\x01aY)V[\x87Q\x92\x01\x91aZ\x83\x81\x84\x84\x8B\x01aY)V[\x86Q\x92\x01\x91aZ\x95\x81\x84\x84\x8A\x01aY)V[\x85Q\x92\x01\x91aZ\xA7\x81\x84\x84\x89\x01aY)V[\x91\x90\x91\x01\x99\x98PPPPPPPPPV[`\0\x83QaZ\xCA\x81\x84` \x88\x01aY)V[\x83Q\x90\x83\x01\x90aZ\xDE\x81\x83` \x88\x01aY)V[\x01\x94\x93PPPPV[`\0\x87QaZ\xF9\x81\x84` \x8C\x01aY)V[\x91\x90\x91\x01\x95\x86RP` \x85\x01\x93\x90\x93R`@\x84\x01\x91\x90\x91R``\x83\x01R`\x80\x82\x01R`\xA0\x01\x91\x90PV[`\0\x84Qa[5\x81\x84` \x89\x01aY)V[\x84Q\x90\x83\x01\x90a[I\x81\x83` \x89\x01aY)V[\x84Q\x91\x01\x90a[\\\x81\x83` \x88\x01aY)V[\x01\x95\x94PPPPPV[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a[\x99WcNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[P\x06\x90V[\x81\x81\x03\x81\x81\x11\x15a)\xFDWa)\xFDaY\x80V[`\0\x85Qa[\xC3\x81\x84` \x8A\x01aY)V[\x85Q\x90\x83\x01\x90a[\xD7\x81\x83` \x8A\x01aY)V[\x85Q\x91\x01\x90a[\xEA\x81\x83` \x89\x01aY)V[\x84Q\x91\x01\x90a[\xFD\x81\x83` \x88\x01aY)V[\x01\x96\x95PPPPPPV[`\0\x89Q` a\\\x1B\x82\x85\x83\x8F\x01aY)V[\x8AQ\x91\x84\x01\x91a\\.\x81\x84\x84\x8F\x01aY)V[\x8AQ\x92\x01\x91a\\@\x81\x84\x84\x8E\x01aY)V[\x89Q\x92\x01\x91a\\R\x81\x84\x84\x8D\x01aY)V[\x88Q\x92\x01\x91a\\d\x81\x84\x84\x8C\x01aY)V[\x87Q\x92\x01\x91a\\v\x81\x84\x84\x8B\x01aY)V[\x86Q\x92\x01\x91a\\\x88\x81\x84\x84\x8A\x01aY)V[\x85Q\x92\x01\x91a\\\x9A\x81\x84\x84\x89\x01aY)V[\x91\x90\x91\x01\x9B\x9APPPPPPPPPPPV[`\0\x88Q` a\\\xC0\x82\x85\x83\x8E\x01aY)V[\x89Q\x91\x84\x01\x91a\\\xD3\x81\x84\x84\x8E\x01aY)V[\x89Q\x92\x01\x91a\\\xE5\x81\x84\x84\x8D\x01aY)V[\x88Q\x92\x01\x91a\\\xF7\x81\x84\x84\x8C\x01aY)V[\x87Q\x92\x01\x91a]\t\x81\x84\x84\x8B\x01aY)V[\x86Q\x92\x01\x91a]\x1B\x81\x84\x84\x8A\x01aY)V[\x85Q\x92\x01\x91a]-\x81\x84\x84\x89\x01aY)V[\x91\x90\x91\x01\x9A\x99PPPPPPPPPPV[`\0\x8AQa]Q\x81\x84` \x8F\x01aY)V[\x91\x90\x91\x01\x98\x89RP` \x88\x01\x96\x90\x96R`@\x87\x01\x94\x90\x94R``\x86\x01\x92\x90\x92R`\x80\x85\x01R`\xA0\x84\x01R`\xC0\x83\x01R`\xE0\x82\x01Ra\x01\0\x01\x91\x90PV\xFE\xA1dsolcC\0\x08\x17\0\n"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\x046\x10a\x01RW`\x005`\xE0\x1C\x80c\x01?\xA5\xFC\x14a\x01WW\x80c\r\x8En,\x14a\x01yW\x80c1=\xF7\xB1\x14a\x01\xABW\x80c8+!Z\x14a\x01\xD8W\x80c@\x999\xB7\x14a\x01\xFCW\x80cHG\xAE]\x14a\x02\x1CW\x80cO\x1E\xF2\x86\x14a\x02\x9EW\x80cR\xD1\x90-\x14a\x02\xB1W\x80cTd`\x85\x14a\x02\xC6W\x80cb\x82w3\x14a\x02\xDBW\x80ci\xCCj\x04\x14a\x02\xF1W\x80cpS\xFCQ\x14a\x03\x06W\x80cqP\x18\xA6\x14a\x03\x1BW\x80cvg\x18\x08\x14a\x030W\x80cv\xB6\xB7\xCB\x14a\x03dW\x80c\x7F\x17\xBA\xAD\x14a\x03zW\x80c\x82\xD0\x7F\xF3\x14a\x04-W\x80c\x85\x84\xD2?\x14a\x04BW\x80c\x8D\xA5\xCB[\x14a\x04\x86W\x80c\xA2D\xD5\x96\x14a\x04\x9BW\x80c\xA5\x1Eo\xEA\x14a\x04\xBBW\x80c\xAA\x92'2\x14a\x04\xDBW\x80c\xAD<\xB1\xCC\x14a\x04\xFBW\x80c\xBD2Q\x9A\x14a\x059W\x80c\xCAo\xE8U\x14a\x05jW\x80c\xDB\x13\xB6\n\x14a\x05\x80W\x80c\xE003\x01\x14a\x05\xBFW\x80c\xF0h T\x14a\x05\xDFW\x80c\xF2\xFD\xE3\x8B\x14a\x06\x11W[`\0\x80\xFD[4\x80\x15a\x01cW`\0\x80\xFD[Pa\x01wa\x01r6`\x04aO\x9DV[a\x061V[\0[4\x80\x15a\x01\x85W`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\xB7W`\0\x80\xFD[P`\x06Ta\x01\xCB\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Qa\x01\xA2\x91\x90aO\xB8V[4\x80\x15a\x01\xE4W`\0\x80\xFD[Pa\x01\xEE`\x03T\x81V[`@Q\x90\x81R` \x01a\x01\xA2V[4\x80\x15a\x02\x08W`\0\x80\xFD[Pa\x01wa\x02\x176`\x04aQ=V[a\x06\xF1V[4\x80\x15a\x02(W`\0\x80\xFD[Pa\x021a\n\x1CV[`@Qa\x01\xA2\x91\x90`\0a\x01\0\x82\x01\x90P`\x01\x80`@\x1B\x03\x80\x84Q\x16\x83R\x80` \x85\x01Q\x16` \x84\x01RP`@\x83\x01Q`@\x83\x01R``\x83\x01Q``\x83\x01R`\x80\x83\x01Q`\x80\x83\x01R`\xA0\x83\x01Q`\xA0\x83\x01R`\xC0\x83\x01Q`\xC0\x83\x01R`\xE0\x83\x01Q`\xE0\x83\x01R\x92\x91PPV[a\x01wa\x02\xAC6`\x04aR\xF7V[a\n\xAEV[4\x80\x15a\x02\xBDW`\0\x80\xFD[Pa\x01\xEEa\n\xCDV[4\x80\x15a\x02\xD2W`\0\x80\xFD[P`\x08Ta\x01\xEEV[4\x80\x15a\x02\xE7W`\0\x80\xFD[Pa\x01\xEE`\x02T\x81V[4\x80\x15a\x02\xFDW`\0\x80\xFD[Pa\x01wa\n\xEAV[4\x80\x15a\x03\x12W`\0\x80\xFD[P`\x07Ta\x01\xEEV[4\x80\x15a\x03'W`\0\x80\xFD[Pa\x01wa\x0BZV[4\x80\x15a\x03F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x14\xD4a\x10^V[\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x81`@Qa\x06\xE6\x91\x90aO\xB8V[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x15]WP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x15Z\x91\x81\x01\x90aUnV[`\x01[a\x15|W\x81`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\x08[\x91\x90aO\xB8V[`\0\x80Q` aYY\x839\x81Q\x91R\x81\x14a\x15\xADW`@Qc*\x87Ri`\xE2\x1B\x81R`\x04\x81\x01\x82\x90R`$\x01a\x08[V[a\x15\xB7\x83\x83a \xB2V[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\x0BXW`@Qcp>F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\x16\x0Fa\x16aV[\x80T`\x01`\x01`\xA0\x1B\x03\x84\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x93\x94P\x91\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x90V[a\x16\xB1a!\x08V[a\x10[\x81a!-V[a\x0BXa!\x08V[\x81Q`\x01`\x01`@\x1B\x03\x16\x15\x15\x80a\x16\xE6WP` \x82\x01Q`\x01`\x01`@\x1B\x03\x16\x15\x15[\x80a\x16\xF3WP`\x80\x82\x01Q\x15[\x80a\x17\0WP`\xA0\x82\x01Q\x15[\x80a\x17\rWP`\xC0\x82\x01Q\x15[\x80a\x17\x1AWP`\xE0\x82\x01Q\x15[\x80a\x17)WPc\xFF\xFF\xFF\xFF\x81\x16\x15[\x15a\x17GW`@QcP\xDD\x03\xF7`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x81`\x05`\0\x80`\x04\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP\x81`\x05`\0\x80`\x08\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP`\0\x80`\x0Ca\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP\x80`\0\x80a\x01\0\n\x81T\x81c\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83c\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`\0a\x19-\x83a\x0E\xBDV[`\x01\x81\x81U`\xE0\x85\x01Q`\x02\x81\x81U`\x03\x93\x90\x93U`\x04U`\x07\x80T\x80\x83\x01\x82U`\0\x91\x82RC`\0\x80Q` aY\x19\x839\x81Q\x91R\x90\x91\x01U`@\x80Q\x80\x82\x01\x82R` \x80\x89\x01Q`\x01`\x01`@\x1B\x03\x90\x81\x16\x83R\x92\x90\x98\x01Q\x97\x81\x01\x97\x88R`\x08\x80T\x94\x85\x01\x81U\x90\x92R\x90Q`\0\x80Q` aY\x99\x839\x81Q\x91R\x92\x90\x93\x02\x91\x82\x01\x80T`\x01`\x01`@\x1B\x03\x19\x16\x93\x90\x91\x16\x92\x90\x92\x17\x90\x91U\x92Q`\0\x80Q` aY\xD9\x839\x81Q\x91R\x90\x93\x01\x92\x90\x92UPPV[a\x19\xEDaM\xF2V[b\x10\0\0\x81R`\x08` \x82\x01R\x7F \xC9@13\xDF\xDE\x9A\x9D8-\xF7o\xB0R5qd\x87%\xAB\xC0\xA7\xC1(0\xBBi\x0E\xC8;3`@\x82\x01QR\x7F\x03\xA0\xA9\xAC\xC3\xE3\x81Z~\xD6\xCB\x13y\xF7\xD1W\xE641dr\x93v9*i:\xCB\xD3\xEC(<` `@\x83\x01Q\x01R\x7F(f\xC1\x8A\xD1\xDF\x10\xEF\x13T,\xCEbP\xCE\x02\xCB*kr\xAE\0\xA9\x85.'\x11\x87\xE9\xE4\xE0\xDB``\x82\x01QR\x7F!\xBE#*B$jVc\xEB\xF4\x83G\x0C\xCAfo\xFE\x9DO\x0Ec\xB9)\xC5\x96\xA7e\x87\x14\xE9p` ``\x83\x01Q\x01R\x7F\x07\xD7xs\xB9\x86\0t\x11\x8Eu\x80\x8CyF\x8B\x83\xC8\xEDd\xBA\x14\xDB\\\xB5\xAF\xA8\xE54\xDE{\x99`\x80\x82\x01QR\x7F\x0B\xE0\xF4H\x83\x90\x80\x13-G\xDE\x17\xDE\0\x99\xB4\xCDt\xAE\x1Ekq\xCD\xDA\x06\xCD\xEB\xB8h\xA5\x0Cm` `\x80\x83\x01Q\x01R\x7F\x13\xBDE\xA0#I\x1E\xAD\xEAD\xCC?$\xCF\xBD\x17\x96\xEA\xDE\x9C\x0E9\xEE\x81\xD9\xF6>\xA0\xA5\x80f%`\xA0\x82\x01QR\x7F\x18\xF9\\\xDD\xA4,\xE1\x1D\x9D\x10\xA3\xB35\xAC\xC2\x14\xE3\x80|W\x8CSY@]\x81\x0C \x8D\xF6\0\x93` `\xA0\x83\x01Q\x01R\x7F\tp\xD9xv4a\xF0\x9E\x9E\xC64T\x074\x978nM(/\xED\xC2\xAC[\x96|\xB9\xFD?\xA8\xA9`\xC0\x82\x01QR\x7F(\xC2!\x7F{\xAC\xF6\xF8\xB2\xB8\xEEJ\x90\xFC\xF8\xB5\xBC\xA0B\x05\xEA\x84\xE8\xE1\xEBT\xB8]\xD4\x1B\xDE(` `\xC0\x83\x01Q\x01R\x7F\x02\xFE=\x02\x98\x8D\xB7\x188\0R\x97\n\xBAF\xA3)m\xF5\xF2\x9Bsk\xA1\xF2\xC4\xCC\xFF\xC8\xB5\x96\x93`\xE0\x82\x01QR\x7F ,>9\x0C\xEE|\\\x85%\xDA#)\xA1\x9FI6\xF6\xF7\x1C\xA9}\xDElo\xA3+8-Z\xCC\x03` `\xE0\x83\x01Q\x01R\x7F#\xAC\x10\xAEl\xA5\xCA\xCE\xE8tK\xB99\xAA\xA859\tT\xB9\x1A\xE6h\xA2\xC8\xD0\xED\xDAU\x8A\x89\xE7a\x01\0\x82\x01QR\x7F\x1C\x8C+\x85l\xDA\xDE%k\xA3#\x7F9\xAF\xD5\xE1p\xA9S \x12\xF7\xAE\xCA\xE4\x9DE\x9B)\xF6\xF6\xAD` a\x01\0\x83\x01Q\x01R\x7F\x16\xEC\x03\xD2`\xBDz\xC1\xC5\x0F\xFAcV]Rt\xB4X,\xEE\xA5/\xF4\x0B\x81\xCD\xFE\x8FDO\x01\xE4a\x01 \x82\x01QR\x7F)9!Rr0\x97\xE0q\x13\xC3\xD7xm$^\xC4\x0C0\x92\x80\x15\xCDP\xB5f\x8AON\xA1p1` a\x01 \x83\x01Q\x01R\x7F,\xDB\xFB:@S\xC8H\x9B\x0C\x94\xE7C8\xAC\x19\x11\x8D\xF7\xA0k\xC5k\x1E\xB4\xD0\xE0\xDCN\xAErHa\x01@\x82\x01QR\x7F\x07\xFE\xA1'\xDA\xE9C\xB8\xDC\x14\x8F\x14\x08\xD4\x0C\xFFF\\\x9CG!\x946i\xB1\xE4\xFDZ9\xDBp6` a\x01@\x83\x01Q\x01R\x7F\x03\x14U\xA7\x9A.\x0C\xE7\x8Al\xB55&\xEC\x04\xAC\x19qj\x86\xB0\x8A\x93\xDFH\xD1x\xF8\xB7~V\x19a\x01`\x82\x01QR\x7F\x11\x86#\xE6\xBC\x13n\xE6\xD3\xF9\x90|\xD4\xAD\x04\xA9A\x8E\xA0;\xA9\x9A\xD7S\"|\xDF\xEEY\x8E\x84\x15` a\x01`\x83\x01Q\x01R\x7F\x08a\xD1\x99wa\xA8R\"j\xAC{\xA9q{\xF6\xAEVE\x10\x99\xBEwL\xDF\x02\xEF5*X\xCB\xC8a\x01\x80\x82\x01QR\x7F\x08\x05\xE3\x92\xBC\xBC\x12\xE4\nr'xc-s\xFE\x98\x1EK\xC6\xFAm\x11x\xB7\n\xF7\xBE\x1C\xB9\xA3\xA3` a\x01\x80\x83\x01Q\x01R\x7F\x10\x1D\x1E9x\xCB\x9F\x1E0=A1D\xEB\xE6v\x82\xC9\xEB\x0C\xFE\x11$)Y\xAA`)\xD7\x8C\xDB\xBCa\x01\xA0\x82\x01QR\x7F\x08\x9E\xB9\xC7'\xE6\xCB\x07\x08+\xC3\xE6\xF4\x0C\xF0OC\x9F\xE4\x80\0`+XGt\xDA\xD7\xEF\xC6`|` a\x01\xA0\x83\x01Q\x01R\x7F-H\x9F$\x93&:\xA8s\xBC\xD9O!\xEF\xB4[\xF2W\xA6\x1D\x81\xC0\xC9\\2\x97\x91e\x06e;@a\x01\xC0\x82\x01QR\x7F\x18\xE4]bz\xAD\xD4\xDF'\x94\xEC\xD9\x90\x9F\xAC\x1Au?\x0Co\xA8\xA9\xC6eJzX\xB0\x91/\xFF\xD5` a\x01\xC0\x83\x01Q\x01R\x7F\x0EC\xE3\xA4\xB1<\xB48\xE2\xAD\x92F\x14&\x1A\xD0$\x02\x14\xFA\x1C\x83\xFC\xDAj\x0B\xF7y\xEB9\xFF\xC5a\x01\xE0\x82\x01QR\x7F\x0E\xAB\xA9\xF4)\xC5\xF6\xFC1\x03\xD4\xCC@V\xC5\0\xFFBB]\x8Ede\xC5\xB8\xE1E!\x9F\x9C\\\xD3` a\x01\xE0\x83\x01Q\x01R\x7F)\xAE5\x1D\t\xDC\xF4\x1C\n\x80\xAB\x059785\x8B\xAA\xB3~o\xBCFK;\xB12X\x99J\x1F\xA4a\x02\0\x82\x01QR\x7F+{\xC7F\x08\xD7\xEC}\xAD\xD0Y}j@\x10\xD8\xBF\xC2\xB3\x19\0(\x19\x01\xCE\xDCB\xBD\xBB\x0F\xB8\xFC` a\x02\0\x83\x01Q\x01R\x7F\x06h\x02\xC7\xCE\xB9\xE9\x13\xD4\xF6T3\xA2\x06a\xE0\x97\xAC\xAC\x1A\xFF\xEC\xBBSJT\xF7j)x\"&a\x02 \x82\x01QR\x7F'\xEC\x80\xE8\x11\xE66\xF34\x82g\x92<\x8Ed\x1B\xD9\x8A~7\xC5!fp\xCB\xFF\x14\xAE2?\x9E\x0E` a\x02 \x83\x01Q\x01R\x7F\x12`M\x1F\x87\xC5\x83\xF6\xC9q\x0Cs\xEA\xF5\x90\xAF\x9D\x07\xAAt=\x13\x81\xD0\xE9\xDF\xF0\xEA\xB2aB9a\x02@\x82\x01QR\x7F\x15\x88W\x9El3x\xEA2\xCBd\x12\x05\xEFv*c\xCD5:\x0B\xD6p9E(\xAD \x81\xEE\x8D\xD4` a\x02@\x83\x01Q\x01R\x7F$}e&\x1D:J\xB0B\xBA\x93s1\xF6\xD0\xC0\xC5\xEB\x9E\xA7\x87S\xA9 \x84\xDB\x1Ai9\xE1\x9E\x82a\x02`\x82\x01QR\x7F,\xE6\xCCfJ2\x14{\xFEj\x0C\x94\xA9[\xF0Ify@\\\xCA\xE0\x16H\xCDN\xC0!\x14Q \xD5` a\x02`\x83\x01Q\x01R\x90V[`\0a\x1F\xD2\x82a!5V[a\x1F\xF5\x83`\0\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[` \x02` \x01\x01Qa\x10\x90V[a \x0B\x83`\x01\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a !\x83`\x02\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a 7\x83`\x03\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a M\x83`\x04\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a c\x83`\x05\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a y\x83`\x06\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[a \x8F\x83`\x07\x81Q\x81\x10a\x1F\xE8Wa\x1F\xE8aU!V[`\0a \x9C\x85\x85\x85a\"mV[\x90Pa \xA7\x81a#\xB7V[\x91PP[\x93\x92PPPV[a \xBB\x82a(\x16V[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a!\0Wa\x15\xB7\x82\x82a(rV[a\n\xC9a(\xE8V[a!\x10a)\x07V[a\x0BXW`@Qc\x1A\xFC\xD7\x9F`\xE3\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x10(a!\x08V[\x80Qa!@\x90a)!V[a!M\x81` \x01Qa)!V[a!Z\x81`@\x01Qa)!V[a!g\x81``\x01Qa)!V[a!t\x81`\x80\x01Qa)!V[a!\x81\x81`\xA0\x01Qa)!V[a!\x8E\x81`\xC0\x01Qa)!V[a!\x9B\x81`\xE0\x01Qa)!V[a!\xA9\x81a\x01\0\x01Qa)!V[a!\xB7\x81a\x01 \x01Qa)!V[a!\xC5\x81a\x01@\x01Qa)!V[a!\xD3\x81a\x01`\x01Qa)!V[a!\xE1\x81a\x01\x80\x01Qa)!V[a!\xEF\x81a\x01\xA0\x01Qa\x10\x90V[a!\xFD\x81a\x01\xC0\x01Qa\x10\x90V[a\"\x0B\x81a\x01\xE0\x01Qa\x10\x90V[a\"\x19\x81a\x02\0\x01Qa\x10\x90V[a\"'\x81a\x02 \x01Qa\x10\x90V[a\"5\x81a\x02@\x01Qa\x10\x90V[a\"C\x81a\x02`\x01Qa\x10\x90V[a\"Q\x81a\x02\x80\x01Qa\x10\x90V[a\"_\x81a\x02\xA0\x01Qa\x10\x90V[a\x10[\x81a\x02\xC0\x01Qa\x10\x90V[a\"uaN\xF6V[\x83` \x01Q\x83Q\x14a\"\x9AW`@Qc \xFA\x9D\x89`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\"\xA7\x85\x85\x85a)\xAFV[\x90P`\0a\"\xB8\x86`\0\x01Qa,\xCEV[\x90P`\0a\"\xCB\x82\x84`\xA0\x01Q\x88a0\x99V[`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x03\xC0\x806\x837PP`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[a#\x16aO0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a#\x0EW\x90PP\x90P`\0a#;\x8A\x85\x8A\x89\x87\x87a0\xF9V[`\xA0\x87\x01Q``\x87\x01Q\x91\x92P\x90`\0\x80Q` aYy\x839\x81Q\x91R`\0\x81\x83\x85\t`@\x80Qa\x01\0\x81\x01\x82R`\xE0\x9C\x8D\x01Q\x81R` \x81\x01\x96\x90\x96R\x85\x01RPPP``\x81\x01\x91\x90\x91R`\x80\x81\x01\x92\x90\x92R`\xA0\x82\x01Ra\x01`\x86\x01Q`\xC0\x82\x01Ra\x01\x80\x90\x95\x01Q\x92\x85\x01\x92\x90\x92RP\x91\x94\x93PPPPV[`\0`\0\x80Q` aYy\x839\x81Q\x91Ra#\xD0aO0V[a#\xD8aO0V[`@\x80Q`\x02\x80\x82R``\x82\x01\x83R`\0\x92` \x83\x01\x90\x806\x837PP`@\x80Q`\x02\x80\x82R``\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[a$\x1AaO0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a$\x12W\x90PP\x90P`\0`\x01\x90P\x80\x83`\0\x81Q\x81\x10a$IWa$IaU!V[` \x02` \x01\x01\x81\x81RPP\x87`\xC0\x01Q\x82`\0\x81Q\x81\x10a$mWa$maU!V[` \x02` \x01\x01\x81\x90RP\x87`\0\x01Q\x83`\x01\x81Q\x81\x10a$\x90Wa$\x90aU!V[` \x02` \x01\x01\x81\x81RPP\x87`\xE0\x01Q\x82`\x01\x81Q\x81\x10a$\xB4Wa$\xB4aU!V[` \x02` \x01\x01\x81\x90RPa$\xC9\x82\x84a1.V[`\x80\x89\x01QQ\x90\x95P``\x93P\x83\x92P\x90P`\0a$\xE8\x82`\x02aU\x87V[a$\xF3\x90`\x01aU\x87V[\x90P\x80`\x01`\x01`@\x1B\x03\x81\x11\x15a%\rWa%\raO\xCCV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a%6W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x80`\x01`\x01`@\x1B\x03\x81\x11\x15a%QWa%QaO\xCCV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a%\x8AW\x81` \x01[a%waO0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a%oW\x90P[P\x92PPP`\0\x80`\0[\x89`\x80\x01QQ\x81\x10\x15a&.W\x89`\x80\x01Q\x81\x81Q\x81\x10a%\xB8Wa%\xB8aU!V[` \x02` \x01\x01Q\x85\x83\x81Q\x81\x10a%\xD2Wa%\xD2aU!V[` \x02` \x01\x01\x81\x81RPP\x89`\xA0\x01Q\x81\x81Q\x81\x10a%\xF4Wa%\xF4aU!V[` \x02` \x01\x01Q\x84\x83\x81Q\x81\x10a&\x0EWa&\x0EaU!V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra&$`\x01\x83aU\x87V[\x91P`\x01\x01a%\x95V[P\x88` \x01Q\x84\x82\x81Q\x81\x10a&FWa&FaU!V[` \x02` \x01\x01\x81\x81RPP\x88`\xC0\x01Q\x83\x82\x81Q\x81\x10a&iWa&iaU!V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra&\x7F`\x01\x82aU\x87V[\x89Q`@\x8B\x01Q\x91\x92P\x90`\0\x89\x82\x84\t\x90P\x80\x87\x85\x81Q\x81\x10a&\xA5Wa&\xA5aU!V[` \x02` \x01\x01\x81\x81RPPPPP\x88`\xE0\x01Q\x83\x82\x81Q\x81\x10a&\xCBWa&\xCBaU!V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra&\xE1`\x01\x82aU\x87V[``\x8A\x01Q\x90\x91P\x87\x81\x84\x08\x92PPa&\xF9\x82a2\x1CV[\x84\x82\x81Q\x81\x10a'\x0BWa'\x0BaU!V[` \x02` \x01\x01\x81\x81RPPa'\x1Fa2NV[\x83\x82\x81Q\x81\x10a'1Wa'1aU!V[` \x02` \x01\x01\x81\x90RPa'Na'I\x84\x86a1.V[a2oV[\x94PPPPP`\0`@Q\x80`\x80\x01`@R\x80\x7F\x01\x18\xC4\xD5\xB87\xBC\xC2\xBC\x89\xB5\xB3\x98\xB5\x97N\x9FYD\x07;2\x07\x8B~#\x1F\xEC\x93\x88\x83\xB0\x81R` \x01\x7F&\x0E\x01\xB2Q\xF6\xF1\xC7\xE7\xFFNX\x07\x91\xDE\xE8\xEAQ\xD8z5\x8E\x03\x8BN\xFE0\xFA\xC0\x93\x83\xC1\x81R` \x01\x7F\"\xFE\xBD\xA3\xC0\xC0c*VG[B\x14\xE5a^\x11\xE6\xDD?\x96\xE6\xCE\xA2\x85J\x87\xD4\xDA\xCC^U\x81R` \x01\x7F\x04\xFCci\xF7\x11\x0F\xE3\xD2QV\xC1\xBB\x9Ar\x85\x9C\xF2\xA0FA\xF9\x9B\xA4\xEEA<\x80\xDAj_\xE4\x81RP\x90Pa(\x0C\x83\x82\x84a(\x07a2\xDCV[a3\xADV[\x96\x95PPPPPPV[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a(CW\x80`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\x08[\x91\x90aO\xB8V[`\0\x80Q` aYY\x839\x81Q\x91R\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa(\x8F\x91\x90aU\x9AV[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a(\xCAW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a(\xCFV[``\x91P[P\x91P\x91Pa(\xDF\x85\x83\x83a4\x90V[\x95\x94PPPPPV[4\x15a\x0BXW`@Qc\xB3\x98\x97\x9F`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a)\x11a\x16\x85V[T`\x01`@\x1B\x90\x04`\xFF\x16\x91\x90PV[`\0`\0\x80Q` aX\xF9\x839\x81Q\x91Ra);\x83a4\xE3V[\x15a)EWPPPV[\x82Q` \x84\x01Q\x82`\x03\x84\x85\x85\x86\t\x85\t\x08\x83\x82\x83\t\x14\x83\x82\x10\x84\x84\x10\x16\x16\x93PPP\x81a\x15\xB7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x17`$\x82\x01Rv\x10\x9B\x8C\x8DM\x0E\x88\x1A[\x9D\x98[\x1AY\x08\x11\xCCH\x1C\x1B\xDA[\x9D`J\x1B`D\x82\x01R`d\x01a\x08[V[a)\xF7`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@\x80Q\x80\x82\x01\x90\x91R``\x81R`\0` \x82\x01R`\0\x80Q` aYy\x839\x81Q\x91Ra*&\x82\x87\x87a4\xF2V[\x81Q\x84Qa*3\x90a8\x8BV[a*@\x86` \x01Qa8\x8BV[a*M\x87`@\x01Qa8\x8BV[a*Z\x88``\x01Qa8\x8BV[a*g\x89`\x80\x01Qa8\x8BV[`@Q` \x01a*|\x96\x95\x94\x93\x92\x91\x90aU\xB6V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra*\x97\x82a9\0V[Pa*\xA1\x82a9\0V[``\x84\x01Ra*\xAF\x82a9\0V[`\x80\x84\x01R\x81Q`\xA0\x85\x01Qa*\xC4\x90a8\x8BV[`@Q` \x01a*\xD5\x92\x91\x90aV5V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra*\xF0\x82a9\0V[\x83R\x81Q`\xC0\x85\x01Qa+\x02\x90a8\x8BV[a+\x0F\x86`\xE0\x01Qa8\x8BV[a+\x1D\x87a\x01\0\x01Qa8\x8BV[a++\x88a\x01 \x01Qa8\x8BV[a+9\x89a\x01@\x01Qa8\x8BV[`@Q` \x01a+N\x96\x95\x94\x93\x92\x91\x90aU\xB6V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra+i\x82a9\0V[`\xA0\x84\x01R\x81Qa\x01\xA0\x85\x01Qa+\x7F\x90a9bV[a+\x8D\x86a\x01\xC0\x01Qa9bV[a+\x9B\x87a\x01\xE0\x01Qa9bV[a+\xA9\x88a\x02\0\x01Qa9bV[a+\xB7\x89a\x02 \x01Qa9bV[`@Q` \x01a+\xCC\x96\x95\x94\x93\x92\x91\x90aVdV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x83Ra\x02@\x85\x01Qa+\xEE\x90a9bV[a+\xFC\x86a\x02`\x01Qa9bV[a,\n\x87a\x02\x80\x01Qa9bV[a,\x18\x88a\x02\xA0\x01Qa9bV[a,&\x89a\x02\xC0\x01Qa9bV[`@Q` \x01a,;\x96\x95\x94\x93\x92\x91\x90aVdV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,V\x82a9\0V[`\xC0\x84\x01R\x81Qa\x01`\x85\x01Qa,l\x90a8\x8BV[a,z\x86a\x01\x80\x01Qa8\x8BV[`@Q` \x01a,\x8C\x93\x92\x91\x90aV\xA0V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\xA7\x82a9\0V[`\xE0\x84\x01R\x82Q\x81\x81\x80\t\x82\x82\x82\t` \x86\x01\x91\x90\x91R`@\x85\x01RP\x91\x95\x94PPPPPV[a-\0`@Q\x80`\xA0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[\x81b\x01\0\0\x03a-\x94WP`@\x80Q`\xA0\x81\x01\x82R`\x10\x81R` \x81\x01\x92\x90\x92R\x7F0d\x1E\x0E\x92\xBE\xBE\xF8\x18&\x8Df;\xCA\xD6\xDB\xCF\xD6\xC0\x14\x91p\xF6\xD7\xD3P\xB1\xB1\xFAl\x10\x01\x90\x82\x01R~\xEE\xB2\xCBY\x81\xEDEd\x9A\xBE\xBD\xE0\x81\xDC\xFF\x16\xC8`\x1D\xE44~}\xD1b\x8B\xA2\xDA\xACC\xB7``\x82\x01R\x7F\x0B]V\xB7\x7F\xE7\x04\xE8\xE9#8\xC0\x08/7\xE0\x91\x12d\x14\xC80\xE4\xC6\x92-Z\xC8\x02\xD8B\xD4`\x80\x82\x01R\x90V[\x81b\x02\0\0\x03a.)WP`@\x80Q`\xA0\x81\x01\x82R`\x11\x81R` \x81\x01\x92\x90\x92R\x7F0d6@\xB9\xF8/\x90\xE8;i\x8E^\xA6\x17\x9C|\x05T.\x85\x953\xB4\x8B\x99S\xA2\xF56\x08\x01\x90\x82\x01R\x7F\x1B\xF8-\xEB\xA7\xD7I\x02\xC3p\x8C\xC6\xE7\x0Ea\xF3\x05\x12\xEC\xA9VU!\x0E'nXX\xCE\x8FX\xE5``\x82\x01R\x7F$L\xF0\x10\xC4<\xA8r7\xD8\xB0\x0B\xF9\xDDP\xC4\xC0\x1C\x7F\x08k\xD4\xE8\xC9 \xE7RQ\xD9o\r\"`\x80\x82\x01R\x90V[\x81b\x04\0\0\x03a.\xBEWP`@\x80Q`\xA0\x81\x01\x82R`\x12\x81R` \x81\x01\x92\x90\x92R\x7F0dBY\xCD\x94\xE7\xDDPE\xD7\xA2p\x13\xB7\xFC\xD2\x1C\x9E;\x7F\xA7R\"\xE7\xBD\xA4\x9Br\x9B\x04\x01\x90\x82\x01R\x7F\x19\xDD\xBC\xAF:\x8DF\xC1\\\x01v\xFB\xB5\xB9^M\xC5p\x88\xFF\x13\xF4\xD1\xBD\x84\xC6\xBF\xA5}\xCD\xC0\xE0``\x82\x01R\x7F\x03hS\xF0\x83x\x0E\x87\xF8\xD7\xC7\x1D\x11\x11\x19\xC5}\xBE\x11\x8C\"\xD5\xADpz\x821tf\xC5\x17L`\x80\x82\x01R\x90V[\x81b\x08\0\0\x03a/SWP`@\x80Q`\xA0\x81\x01\x82R`\x13\x81R` \x81\x01\x92\x90\x92R\x7F0dHfWcD\x03\x84K\x0E\xACx\xCA\x88,\xFD(CA\xFC\xB0aZ\x15\xCF\xCD\x17\xB1M\x82\x01\x90\x82\x01R\x7F\"`\xE7$\x84K\xCARQ\x82\x93S\x96\x8EI\x150RXA\x83WG:\\\x1DY\x7Fa?l\xBD``\x82\x01R\x7F\x06\xE4\x02\xC0\xA3\x14\xFBg\xA1\\\xF8\x06fJ\xE1\xB7\"\xDB\xC0\xEF\xE6nl\x81\xD9\x8F\x99$\xCASS!`\x80\x82\x01R\x90V[\x81b\x10\0\0\x03a/\xE8WP`@\x80Q`\xA0\x81\x01\x82R`\x14\x81R` \x81\x01\x92\x90\x92R\x7F0dKl\x9CJr\x16\x9EM\xAA1}%\xF0E\x12\xAE\x15\xC5;4\xE8\xF5\xAC\xD8\xE1U\xD0\xA6\xC1\x01\x90\x82\x01R\x7F&\x12]\xA1\n\x0E\xD0c'P\x8A\xBA\x06\xD1\xE3\x03\xACaf2\xDB\xED4\x9FSB-\xA9S3xW``\x82\x01R\x7F\x10\x0C3-!\0\x89_\xABds\xBC,Q\xBF\xCAR\x1FE\xCB;\xAC\xA6&\x08R\xA8\xFD\xE2l\x91\xF3`\x80\x82\x01R\x90V[\x81` \x03a0{WP`@\x80Q`\xA0\x81\x01\x82R`\x05\x81R` \x81\x01\x92\x90\x92R\x7F.\xE1+\xFFJ(\x13(j\x8D\xC3\x88\xCDuM\x9A>\xF2I\x065\xEB\xA5\x0C\xB9\xC2\xE5\xE7P\x80\0\x01\x90\x82\x01R\x7F\t\xC52\xC60k\x93\xD2\x96x \rG\xC0\xB2\xA9\x9C\x18\xD5\x1B\x83\x8E\xEB\x1D>\xEDLS;\xB5\x12\xD0``\x82\x01R\x7F'$q6\x03\xBF\xBDy\n\xEA\xF3\xE7\xDF%\xD8\xE7\xEF\x8F1\x134\x90[M\x8C\x99\x98\x0C\xF2\x10\x97\x9D`\x80\x82\x01R\x90V[`@Qc\xE2\xEF\t\xE5`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x91\x90PV[a0\xBD`@Q\x80``\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[a0\xC7\x84\x84a:\x99V[\x80\x82Ra0\xD7\x90\x85\x90\x85\x90a:\xEDV[` \x82\x01R\x80Qa0\xED\x90\x85\x90\x84\x90\x86\x90a;aV[`@\x82\x01R\x93\x92PPPV[`\0\x80a1\x07\x85\x87\x89a=\x15V[\x90Pa1\x17\x88\x86\x89\x89\x88\x88a>\x01V[a1\"\x81\x87\x86aA\rV[\x98\x97PPPPPPPPV[a16aO0V[\x82Q\x82Q\x14a1\x87W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FMSM error: length does not match`D\x82\x01R`d\x01a\x08[V[a1\xC5\x83`\0\x81Q\x81\x10a1\x9DWa1\x9DaU!V[` \x02` \x01\x01Q\x83`\0\x81Q\x81\x10a1\xB8Wa1\xB8aU!V[` \x02` \x01\x01QaA]V[\x90P`\x01[\x82Q\x81\x10\x15a2\x15Wa2\x0B\x82a2\x06\x86\x84\x81Q\x81\x10a1\xECWa1\xECaU!V[` \x02` \x01\x01Q\x86\x85\x81Q\x81\x10a1\xB8Wa1\xB8aU!V[aA\xF1V[\x91P`\x01\x01a1\xCAV[P\x92\x91PPV[`\0a26`\0\x80Q` aYy\x839\x81Q\x91R\x83aV\xF9V[a\x10\x1A\x90`\0\x80Q` aYy\x839\x81Q\x91RaU\x0EV[a2VaO0V[P`@\x80Q\x80\x82\x01\x90\x91R`\x01\x81R`\x02` \x82\x01R\x90V[a2waO0V[a2\x80\x82a4\xE3V[\x15a2\x89WP\x90V[`@Q\x80`@\x01`@R\x80\x83`\0\x01Q\x81R` \x01`\0\x80Q` aX\xF9\x839\x81Q\x91R\x84` \x01Qa2\xBC\x91\x90aV\xF9V[a2\xD4\x90`\0\x80Q` aX\xF9\x839\x81Q\x91RaU\x0EV[\x90R\x92\x91PPV[a3\x07`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80`\x80\x01`@R\x80\x7F\x18\0\xDE\xEF\x12\x1F\x1EvBj\0f^\\DygC\"\xD4\xF7^\xDA\xDDF\xDE\xBD\\\xD9\x92\xF6\xED\x81R` \x01\x7F\x19\x8E\x93\x93\x92\rH:r`\xBF\xB71\xFB]%\xF1\xAAI35\xA9\xE7\x12\x97\xE4\x85\xB7\xAE\xF3\x12\xC2\x81R` \x01\x7F\x12\xC8^\xA5\xDB\x8Cm\xEBJ\xABq\x80\x8D\xCB@\x8F\xE3\xD1\xE7i\x0CC\xD3{L\xE6\xCC\x01f\xFA}\xAA\x81R` \x01\x7F\t\x06\x89\xD0X_\xF0u\xEC\x9E\x99\xADi\x0C3\x95\xBCK13p\xB3\x8E\xF3U\xAC\xDA\xDC\xD1\"\x97[\x81RP\x90P\x90V[`\0\x80`\0`@Q\x87Q\x81R` \x88\x01Q` \x82\x01R` \x87\x01Q`@\x82\x01R\x86Q``\x82\x01R``\x87\x01Q`\x80\x82\x01R`@\x87\x01Q`\xA0\x82\x01R\x85Q`\xC0\x82\x01R` \x86\x01Q`\xE0\x82\x01R` \x85\x01Qa\x01\0\x82\x01R\x84Qa\x01 \x82\x01R``\x85\x01Qa\x01@\x82\x01R`@\x85\x01Qa\x01`\x82\x01R` `\0a\x01\x80\x83`\x08Z\xFA\x91PP`\0Q\x91P\x80a4\x82W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1C`$\x82\x01R{Bn254: Pairing check failed!` \x1B`D\x82\x01R`d\x01a\x08[V[P\x15\x15\x90P[\x94\x93PPPPV[``\x82a4\xA5Wa4\xA0\x82aB\x8CV[a \xABV[\x81Q\x15\x80\x15a4\xBCWP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a4\xDCW\x83`@Qc\x99\x96\xB3\x15`\xE0\x1B\x81R`\x04\x01a\x08[\x91\x90aO\xB8V[P\x80a \xABV[\x80Q` \x90\x91\x01Q\x15\x90\x15\x16\x90V[\x82Q`\xFE\x90a5-a5\x03\x83a9bV[`@Q` \x01a5\x15\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x04aB\xB5V[a5ga5=\x86`\0\x01Qa9bV[`@Q` \x01a5O\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x08aB\xB5V[a5wa5=\x87` \x01Qa9bV[`@Q` \x01a5\x8A\x94\x93\x92\x91\x90aW\x1BV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85Ra5\xA7`\x01a9bV[a5\xBE`\0\x80Q` aZ\x19\x839\x81Q\x91Ra9bV[a5\xD5`\0\x80Q` aY\xB9\x839\x81Q\x91Ra9bV[a5\xEC`\0\x80Q` aY\xF9\x839\x81Q\x91Ra9bV[a6\x03`\0\x80Q` aY9\x839\x81Q\x91Ra9bV[`@Q` \x01a6\x18\x96\x95\x94\x93\x92\x91\x90aVdV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85R`\xE0\x84\x01Qa69\x90a8\x8BV[a6G\x85a\x01\0\x01Qa8\x8BV[a6U\x86a\x01 \x01Qa8\x8BV[a6c\x87a\x01@\x01Qa8\x8BV[a6q\x88a\x01`\x01Qa8\x8BV[a6\x7F\x89a\x01\x80\x01Qa8\x8BV[a6\x8D\x8Aa\x01\xE0\x01Qa8\x8BV[`@Q` \x01a6\xA4\x98\x97\x96\x95\x94\x93\x92\x91\x90aWrV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85Ra\x02\0\x84\x01Qa6\xC6\x90a8\x8BV[a6\xD4\x85a\x02 \x01Qa8\x8BV[a6\xE2\x86a\x02@\x01Qa8\x8BV[a6\xF0\x87a\x01\xA0\x01Qa8\x8BV[a6\xFE\x88a\x01\xC0\x01Qa8\x8BV[a7\x0C\x89a\x02`\x01Qa8\x8BV[`@Q` \x01a7\"\x97\x96\x95\x94\x93\x92\x91\x90aX\x17V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x81R\x81\x86R\x84\x01Qa7A\x90a8\x8BV[a7N\x85``\x01Qa8\x8BV[a7[\x86`\x80\x01Qa8\x8BV[a7h\x87`\xA0\x01Qa8\x8BV[a7u\x88`\xC0\x01Qa8\x8BV[`@Q` \x01a7\x8A\x96\x95\x94\x93\x92\x91\x90aU\xB6V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85R\x82Qa7\xC1\x90\x84\x90`\0\x90a7\xB4Wa7\xB4aU!V[` \x02` \x01\x01Qa9bV[a7\xD7\x84`\x01\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a7\xED\x85`\x02\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8\x03\x86`\x03\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8\x19\x87`\x04\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8/\x88`\x05\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8E\x89`\x06\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[a8[\x8A`\x07\x81Q\x81\x10a7\xB4Wa7\xB4aU!V[`@Q` \x01a8s\x99\x98\x97\x96\x95\x94\x93\x92\x91\x90aX\xA9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x90\x93RPPPV[```\0a8\x98\x83a4\xE3V[\x15a8\xA4W`\x01`\xFE\x1B\x17[` \x83\x01Q`\0\x80Q` aX\xF9\x839\x81Q\x91R`\x01\x91\x90\x91\x1B\x10a8\xCAWP`\x01`\xFF\x1B[\x82Qa8\xD7\x90\x82\x17a9bV[`@Q` \x01a8\xE9\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP\x91\x90PV[` \x80\x82\x01Q\x82Q\x80Q`@Q\x83\x81R`\0\x94\x85\x94\x93\x92\x91\x90\x81\x01\x85[\x83\x81\x10\x15a97W` \x81\x86\x01\x81\x01Q\x83\x83\x01R\x01a9\x1DV[PP` \x91\x82\x01\x90 \x90\x86\x01\x81\x90R\x92P`\0a(\x0C`\0\x80Q` aYy\x839\x81Q\x91R\x85aV\xF9V[`\0\x81\x90P`\x08\x81~\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\x16\x90\x1B`\x08\x82\x7F\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\xFF\0\x16\x90\x1C\x17\x90P`\x10\x81}\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\x16\x90\x1B`\x10\x82}\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\0\0\xFF\xFF\x19\x16\x90\x1C\x17\x90P` \x81{\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\x16\x90\x1B` \x82{\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\x19\x16\x90\x1C\x17\x90P`@\x81`\x01`\x01`@\x1B\x03`\x01`\x80\x1B\x03`\x01`\xC0\x1B\x03\x16\x90\x1B`@\x82`\x01`\x01`@\x1B\x03`\x01`\x80\x1B\x03`\x01`\xC0\x1B\x03\x19\x16\x90\x1C\x17\x90P`\x80\x81\x90\x1B`\x80\x82\x90\x1C\x17\x90P\x91\x90PV[\x81Q`\0\x90`\0\x80Q` aYy\x839\x81Q\x91R\x90\x83\x80\x15a:\xDDW\x84\x93P`\0[\x82\x81\x10\x15a:\xD1W\x83\x85\x86\t\x94P`\x01\x01a:\xBBV[P`\x01\x84\x03\x93Pa:\xE4V[`\x01\x83\x03\x93P[PPP\x92\x91PPV[`\0\x82`\x01\x03a:\xFFWP`\x01a \xABV[\x81`\0\x03a;\x0FWP`\0a \xABV[`@\x84\x01Q`\0\x80Q` aYy\x839\x81Q\x91R\x90`\0\x90\x82\x81\x86\t\x90P\x85\x80\x15a;?W`\x01\x87\x03\x92Pa;FV[`\x01\x84\x03\x92P[Pa;P\x82aC\xC2V[\x91P\x82\x82\x82\t\x97\x96PPPPPPPV[\x82Q`\0\x90`\0\x80Q` aYy\x839\x81Q\x91R\x90\x83\x83\x03a;\xE2W`\x01`\0[\x82\x81\x10\x15a;\xD5W\x81\x87\x03a;\xB6W\x87\x81\x81Q\x81\x10a;\xA3Wa;\xA3aU!V[` \x02` \x01\x01Q\x94PPPPPa4\x88V[\x83\x80a;\xC4Wa;\xC4aV\xE3V[\x89``\x01Q\x83\t\x91P`\x01\x01a;\x82V[P`\0\x93PPPPa4\x88V[`\0\x80`\0\x80\x8A`@\x01Q\x90P`\0\x80a;\xFC\x8D\x88aDhV[\x90P`\0\x87`\x01`\x01`@\x1B\x03\x81\x11\x15a<\x18Wa<\x18aO\xCCV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F\x86\x86\x86\x86\x85\x87aE-V[`\xC0\x85\x01Q\x82Q`\0\x80Q` aYy\x839\x81Q\x91R\x91\x90\x81\x90\x81\x90\x86\x90`\x14\x90\x81\x10a>>Wa>>aU!V[` \x02` \x01\x01\x81\x81RPP\x85`\0\x01Q\x84`\x14\x81Q\x81\x10a>bWa>baU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x15\x81Q\x81\x10a>\x87Wa>\x87aU!V[` \x02` \x01\x01\x81\x81RPP\x85` \x01Q\x84`\x15\x81Q\x81\x10a>\xABWa>\xABaU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x16\x81Q\x81\x10a>\xD0Wa>\xD0aU!V[` \x02` \x01\x01\x81\x81RPP\x85`@\x01Q\x84`\x16\x81Q\x81\x10a>\xF4Wa>\xF4aU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x17\x81Q\x81\x10a?\x19Wa?\x19aU!V[` \x02` \x01\x01\x81\x81RPP\x85``\x01Q\x84`\x17\x81Q\x81\x10a?=Wa?=aU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x18\x81Q\x81\x10a?bWa?baU!V[` \x02` \x01\x01\x81\x81RPP\x85`\x80\x01Q\x84`\x18\x81Q\x81\x10a?\x86Wa?\x86aU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x19\x81Q\x81\x10a?\xABWa?\xABaU!V[` \x02` \x01\x01\x81\x81RPP\x88`@\x01Q\x84`\x19\x81Q\x81\x10a?\xCFWa?\xCFaU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1A\x81Q\x81\x10a?\xF4Wa?\xF4aU!V[` \x02` \x01\x01\x81\x81RPP\x88``\x01Q\x84`\x1A\x81Q\x81\x10a@\x18Wa@\x18aU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1B\x81Q\x81\x10a@=Wa@=aU!V[` \x02` \x01\x01\x81\x81RPP\x88`\x80\x01Q\x84`\x1B\x81Q\x81\x10a@aWa@aaU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1C\x81Q\x81\x10a@\x86Wa@\x86aU!V[` \x02` \x01\x01\x81\x81RPP\x88`\xA0\x01Q\x84`\x1C\x81Q\x81\x10a@\xAAWa@\xAAaU!V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x87`\xE0\x01Q\x85`\x1D\x81Q\x81\x10a@\xD3Wa@\xD3aU!V[` \x02` \x01\x01\x81\x81RPP\x85`\xA0\x01Q\x84`\x1D\x81Q\x81\x10a@\xF7Wa@\xF7aU!V[` \x02` \x01\x01\x81\x90RPPPPPPPPPPV[`\0\x80Q` aYy\x839\x81Q\x91R\x83\x81\x03\x90`\0[`\n\x81\x10\x15aATW` `\x15\x82\x01\x02\x84\x01Q` \x82\x02a\x01\xA0\x01\x86\x01Q\x83\x84\x82\x84\t\x86\x08\x94PPP`\x01\x01aA#V[PP\x93\x92PPPV[aAeaO0V[aAmaOJV[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x81\x01\x83\x90R`\0``\x83`\x80\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80aA\x9FW`\0\x80\xFD[P\x80aA\xE9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01RxBn254: scalar mul failed!`8\x1B`D\x82\x01R`d\x01a\x08[V[PP\x92\x91PPV[aA\xF9aO0V[aB\x01aOhV[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x83\x01R\x83\x01Q``\x80\x83\x01\x91\x90\x91R`\0\x90\x83`\xC0\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80aB>W`\0\x80\xFD[P\x80aA\xE9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: group addition failed!\0\0\0`D\x82\x01R`d\x01a\x08[V[\x80Q\x15aB\x9CW\x80Q\x80\x82` \x01\xFD[`@Qc\n\x12\xF5!`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x81aB\xC3\x81`\x1FaU\x87V[\x10\x15aC\x02W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x0E`$\x82\x01Rmslice_overflow`\x90\x1B`D\x82\x01R`d\x01a\x08[V[aC\x0C\x82\x84aU\x87V[\x84Q\x10\x15aCPW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x11`$\x82\x01Rpslice_outOfBounds`x\x1B`D\x82\x01R`d\x01a\x08[V[``\x82\x15\x80\x15aCoW`@Q\x91P`\0\x82R` \x82\x01`@RaC\xB9V[`@Q\x91P`\x1F\x84\x16\x80\x15` \x02\x81\x84\x01\x01\x85\x81\x01\x87\x83\x15` \x02\x84\x8B\x01\x01\x01[\x81\x83\x10\x15aC\xA8W\x80Q\x83R` \x92\x83\x01\x92\x01aC\x90V[PP\x85\x84R`\x1F\x01`\x1F\x19\x16`@RP[P\x94\x93PPPPV[`\0\x80`\0`\0\x80Q` aYy\x839\x81Q\x91R\x90P`@Q` \x81R` \x80\x82\x01R` `@\x82\x01R\x84``\x82\x01R`\x02\x82\x03`\x80\x82\x01R\x81`\xA0\x82\x01R` `\0`\xC0\x83`\x05Z\xFA\x92PP`\0Q\x92P\x81aDaW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: pow precompile failed!\0\0\0`D\x82\x01R`d\x01a\x08[V[PP\x91\x90PV[``\x82` \x01Q\x82\x11\x15aD\x8FW`@Qc\x8C^\x11\xF1`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x83\x01Q`\x01`\0\x80Q` aYy\x839\x81Q\x91R\x84`\x01`\x01`@\x1B\x03\x81\x11\x15aD\xBDWaD\xBDaO\xCCV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15aD\xE6W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x84\x15\x19\x15a:\xE4W` \x84\x01\x85` \x02\x81\x01`\x01\x82R` \x82\x01\x91P[\x80\x82\x10\x15aE\"W\x82\x85\x85\t\x93P\x83\x82R` \x82\x01\x91PaE\x06V[PPPPP\x92\x91PPV[`\0\x80`\0\x80`\0\x80`\0\x80Q` aYy\x839\x81Q\x91R\x90P\x80` \x8B\x01Q` \x8D\x01Q\t\x95P\x8AQ\x93P\x80`\xA0\x8C\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` aZ\x19\x839\x81Q\x91R\x84\t\x91P\x80a\x01\xC0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` aY\xB9\x839\x81Q\x91R\x84\t\x91P\x80a\x01\xE0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` aY\xF9\x839\x81Q\x91R\x84\t\x91P\x80a\x02\0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` aY9\x839\x81Q\x91R\x84\t\x91P\x80a\x02 \x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80\x84\x87\x08\x95P\x88`\xA0\x01Q\x88`\0\x81Q\x81\x10aF\\WaF\\aU!V[` \x02` \x01\x01\x81\x90RP\x85\x87`\0\x81Q\x81\x10aF{WaF{aU!V[` \x02` \x01\x01\x81\x81RPP\x80``\x8C\x01Q\x8CQ\t\x94P\x80a\x02\xC0\x8A\x01Q\x86\t\x94P\x80a\x02@\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02`\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xC0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02\x80\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xE0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02\xA0\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x02\0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x8B`\xC0\x01Q\x88`\x01\x81Q\x81\x10aG]WaG]aU!V[` \x90\x81\x02\x91\x90\x91\x01\x01RaGr\x85\x82aU\x0EV[\x87`\x01\x81Q\x81\x10aG\x85WaG\x85aU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xA0\x01Q\x87`\x02\x81Q\x81\x10aG\xAAWaG\xAAaU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xC0\x01Q\x87`\x03\x81Q\x81\x10aG\xCFWaG\xCFaU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xE0\x01Q\x87`\x04\x81Q\x81\x10aG\xF4WaG\xF4aU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x02\0\x01Q\x87`\x05\x81Q\x81\x10aH\x19WaH\x19aU!V[` \x02` \x01\x01\x81\x81RPP\x8B`\xE0\x01Q\x88`\x02\x81Q\x81\x10aH=WaH=aU!V[` \x02` \x01\x01\x81\x90RP\x8Ba\x01\0\x01Q\x88`\x03\x81Q\x81\x10aHaWaHaaU!V[` \x02` \x01\x01\x81\x90RP\x8Ba\x01 \x01Q\x88`\x04\x81Q\x81\x10aH\x85WaH\x85aU!V[` \x02` \x01\x01\x81\x90RP\x8Ba\x01@\x01Q\x88`\x05\x81Q\x81\x10aH\xA9WaH\xA9aU!V[` \x02` \x01\x01\x81\x90RP\x80a\x01\xC0\x8A\x01Qa\x01\xA0\x8B\x01Q\t\x92P\x82\x87`\x06\x81Q\x81\x10aH\xD8WaH\xD8aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01`\x01Q\x88`\x06\x81Q\x81\x10aH\xFDWaH\xFDaU!V[` \x02` \x01\x01\x81\x90RP\x80a\x02\0\x8A\x01Qa\x01\xE0\x8B\x01Q\t\x92P\x82\x87`\x07\x81Q\x81\x10aI,WaI,aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\x80\x01Q\x88`\x07\x81Q\x81\x10aIQWaIQaU!V[` \x02` \x01\x01\x81\x90RPa\x01\xA0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\x08\x81Q\x81\x10aI\x8AWaI\x8AaU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\xE0\x01Q\x88`\x08\x81Q\x81\x10aI\xAFWaI\xAFaU!V[` \x02` \x01\x01\x81\x90RPa\x01\xC0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\t\x81Q\x81\x10aI\xE8WaI\xE8aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02\0\x01Q\x88`\t\x81Q\x81\x10aJ\rWaJ\raU!V[` \x02` \x01\x01\x81\x90RPa\x01\xE0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\n\x81Q\x81\x10aJFWaJFaU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02 \x01Q\x88`\n\x81Q\x81\x10aJkWaJkaU!V[` \x02` \x01\x01\x81\x90RPa\x02\0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\x0B\x81Q\x81\x10aJ\xA4WaJ\xA4aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02@\x01Q\x88`\x0B\x81Q\x81\x10aJ\xC9WaJ\xC9aU!V[` \x02` \x01\x01\x81\x90RP\x88a\x02 \x01Q\x81aJ\xE5\x91\x90aU\x0EV[\x87`\x0C\x81Q\x81\x10aJ\xF8WaJ\xF8aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\xA0\x01Q\x88`\x0C\x81Q\x81\x10aK\x1DWaK\x1DaU!V[` \x02` \x01\x01\x81\x90RP`\x01\x87`\r\x81Q\x81\x10aK=WaK=aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\xC0\x01Q\x88`\r\x81Q\x81\x10aKbWaKbaU!V[` \x02` \x01\x01\x81\x90RP\x80a\x01\xC0\x8A\x01Qa\x01\xA0\x8B\x01Q\t\x92P\x80a\x01\xE0\x8A\x01Q\x84\t\x92P\x80a\x02\0\x8A\x01Q\x84\t\x92P\x80a\x02 \x8A\x01Q\x84\t\x92P\x82\x87`\x0E\x81Q\x81\x10aK\xB2WaK\xB2aU!V[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02`\x01Q\x88`\x0E\x81Q\x81\x10aK\xD7WaK\xD7aU!V[` \x90\x81\x02\x91\x90\x91\x01\x01R\x89QaK\xEE\x90\x82aU\x0EV[\x87`\x0F\x81Q\x81\x10aL\x01WaL\x01aU!V[` \x02` \x01\x01\x81\x81RPP\x88`\xC0\x01Q\x88`\x0F\x81Q\x81\x10aL%WaL%aU!V[` \x02` \x01\x01\x81\x90RP\x80`\x01\x8BQ\x08`\xA0\x8C\x01Q\x90\x93P\x81\x90\x80\t\x91P\x80\x82\x84\t\x92P\x80\x83` `\x10\x02\x89\x01Q\t\x91P\x81\x87`\x10\x81Q\x81\x10aLkWaLkaU!V[` \x02` \x01\x01\x81\x81RPP\x88`\xE0\x01Q\x88`\x10\x81Q\x81\x10aL\x8FWaL\x8FaU!V[` \x02` \x01\x01\x81\x90RP\x80\x83` `\x11\x02\x89\x01Q\t\x91P\x81\x87`\x11\x81Q\x81\x10aL\xBBWaL\xBBaU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01\0\x01Q\x88`\x11\x81Q\x81\x10aL\xE0WaL\xE0aU!V[` \x02` \x01\x01\x81\x90RP\x80\x83` `\x12\x02\x89\x01Q\t\x91P\x81\x87`\x12\x81Q\x81\x10aM\x0CWaM\x0CaU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01 \x01Q\x88`\x12\x81Q\x81\x10aM1WaM1aU!V[` \x02` \x01\x01\x81\x90RP\x80\x83` `\x13\x02\x89\x01Q\t\x91P\x81\x87`\x13\x81Q\x81\x10aM]WaM]aU!V[` \x02` \x01\x01\x81\x81RPP\x88a\x01@\x01Q\x88`\x13\x81Q\x81\x10aM\x82WaM\x82aU!V[` \x02` \x01\x01\x81\x90RPPPPPPPPPPPPPV[`@Q\x80a\x01\0\x01`@R\x80`\0`\x01`\x01`@\x1B\x03\x16\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x16\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80a\x02\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01aN\x14aO0V[\x81R` \x01aN!aO0V[\x81R` \x01aN.aO0V[\x81R` \x01aN;aO0V[\x81R` \x01aNHaO0V[\x81R` \x01aNUaO0V[\x81R` \x01aNbaO0V[\x81R` \x01aNoaO0V[\x81R` \x01aN|aO0V[\x81R` \x01aN\x89aO0V[\x81R` \x01aN\x96aO0V[\x81R` \x01aN\xA3aO0V[\x81R` \x01aN\xB0aO0V[\x81R` \x01aN\xBDaO0V[\x81R` \x01aN\xCAaO0V[\x81R` \x01aN\xD7aO0V[\x81R` \x01aN\xE4aO0V[\x81R` \x01aN\xF1aO0V[\x90R\x90V[`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01``\x81R` \x01``\x81R` \x01aN\xE4[`@Q\x80`@\x01`@R\x80`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80``\x01`@R\x80`\x03\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`\x80\x01`@R\x80`\x04\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a0\x94W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aO\xAFW`\0\x80\xFD[a \xAB\x82aO\x86V[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Qa\x02\xE0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aP\x05WaP\x05aO\xCCV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aP3WaP3aO\xCCV[`@R\x91\x90PV[\x805`\x01`\x01`@\x1B\x03\x81\x16\x81\x14a0\x94W`\0\x80\xFD[`\0a\x01\0\x80\x83\x85\x03\x12\x15aPfW`\0\x80\xFD[`@Q\x90\x81\x01\x90`\x01`\x01`@\x1B\x03\x82\x11\x81\x83\x10\x17\x15aP\x88WaP\x88aO\xCCV[\x81`@R\x80\x92PaP\x98\x84aP;V[\x81RaP\xA6` \x85\x01aP;V[` \x82\x01R`@\x84\x015`@\x82\x01R``\x84\x015``\x82\x01R`\x80\x84\x015`\x80\x82\x01R`\xA0\x84\x015`\xA0\x82\x01R`\xC0\x84\x015`\xC0\x82\x01R`\xE0\x84\x015`\xE0\x82\x01RPP\x92\x91PPV[`\0`@\x82\x84\x03\x12\x15aQ\x01W`\0\x80\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aQ#WaQ#aO\xCCV[`@R\x825\x81R` \x92\x83\x015\x92\x81\x01\x92\x90\x92RP\x91\x90PV[`\0\x80\x82\x84\x03a\x05\x80\x81\x12\x15aQRW`\0\x80\xFD[aQ\\\x85\x85aPRV[\x92Pa\x01\0a\x04\x80\x80`\xFF\x19\x84\x01\x12\x15aQuW`\0\x80\xFD[aQ}aO\xE2V[\x92PaQ\x8B\x87\x83\x88\x01aP\xEFV[\x83Ra\x01@aQ\x9C\x88\x82\x89\x01aP\xEFV[` \x85\x01Ra\x01\x80aQ\xB0\x89\x82\x8A\x01aP\xEFV[`@\x86\x01Ra\x01\xC0aQ\xC4\x8A\x82\x8B\x01aP\xEFV[``\x87\x01Ra\x02\0aQ\xD8\x8B\x82\x8C\x01aP\xEFV[`\x80\x88\x01Ra\x02@aQ\xEC\x8C\x82\x8D\x01aP\xEFV[`\xA0\x89\x01Ra\x02\x80aR\0\x8D\x82\x8E\x01aP\xEFV[`\xC0\x8A\x01Ra\x02\xC0aR\x14\x8E\x82\x8F\x01aP\xEFV[`\xE0\x8B\x01RaR'\x8Ea\x03\0\x8F\x01aP\xEFV[\x89\x8B\x01RaR9\x8Ea\x03@\x8F\x01aP\xEFV[a\x01 \x8B\x01RaRM\x8Ea\x03\x80\x8F\x01aP\xEFV[\x87\x8B\x01RaR_\x8Ea\x03\xC0\x8F\x01aP\xEFV[a\x01`\x8B\x01RaRs\x8Ea\x04\0\x8F\x01aP\xEFV[\x86\x8B\x01Ra\x04@\x8D\x015a\x01\xA0\x8B\x01Ra\x04`\x8D\x015\x85\x8B\x01R\x87\x8D\x015a\x01\xE0\x8B\x01Ra\x04\xA0\x8D\x015\x84\x8B\x01Ra\x04\xC0\x8D\x015a\x02 \x8B\x01Ra\x04\xE0\x8D\x015\x83\x8B\x01Ra\x05\0\x8D\x015a\x02`\x8B\x01Ra\x05 \x8D\x015\x82\x8B\x01Ra\x05@\x8D\x015a\x02\xA0\x8B\x01Ra\x05`\x8D\x015\x81\x8B\x01RPPPPPPPPP\x80\x91PP\x92P\x92\x90PV[`\0\x80`@\x83\x85\x03\x12\x15aS\nW`\0\x80\xFD[aS\x13\x83aO\x86V[\x91P` \x83\x81\x015`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aS0W`\0\x80\xFD[\x81\x86\x01\x91P\x86`\x1F\x83\x01\x12aSDW`\0\x80\xFD[\x815\x81\x81\x11\x15aSVWaSVaO\xCCV[aSh`\x1F\x82\x01`\x1F\x19\x16\x85\x01aP\x0BV[\x91P\x80\x82R\x87\x84\x82\x85\x01\x01\x11\x15aS~W`\0\x80\xFD[\x80\x84\x84\x01\x85\x84\x017`\0\x84\x82\x84\x01\x01RP\x80\x93PPPP\x92P\x92\x90PV[`\x01`\x01`@\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[\x805c\xFF\xFF\xFF\xFF\x81\x16\x81\x14a0\x94W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aS\xD6W`\0\x80\xFD[a \xAB\x82aS\xB0V[`\0` \x82\x84\x03\x12\x15aS\xF1W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`\0a\x01@\x84\x86\x03\x12\x15aT\x0EW`\0\x80\xFD[aT\x18\x85\x85aPRV[\x92PaT'a\x01\0\x85\x01aS\xB0V[\x91PaT6a\x01 \x85\x01aO\x86V[\x90P\x92P\x92P\x92V[`\0a\x01\0\x82\x84\x03\x12\x15aTRW`\0\x80\xFD[a \xAB\x83\x83aPRV[`\0[\x83\x81\x10\x15aTwW\x81\x81\x01Q\x83\x82\x01R` \x01aT_V[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01RaT\x9F\x81`@\x85\x01` \x87\x01aT\\V[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15aT\xC6W`\0\x80\xFD[PP\x805\x92` \x90\x91\x015\x91PV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01`\x01`@\x1B\x03\x81\x81\x16\x83\x82\x16\x02\x80\x82\x16\x91\x90\x82\x81\x14aA\xE9WaA\xE9aT\xD5V[\x81\x81\x03\x81\x81\x11\x15a\x10\x1AWa\x10\x1AaT\xD5V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81aUFWaUFaT\xD5V[P`\0\x19\x01\x90V[`\x01`\x01`@\x1B\x03\x81\x81\x16\x83\x82\x16\x01\x90\x80\x82\x11\x15a2\x15Wa2\x15aT\xD5V[`\0` \x82\x84\x03\x12\x15aU\x80W`\0\x80\xFD[PQ\x91\x90PV[\x80\x82\x01\x80\x82\x11\x15a\x10\x1AWa\x10\x1AaT\xD5V[`\0\x82QaU\xAC\x81\x84` \x87\x01aT\\V[\x91\x90\x91\x01\x92\x91PPV[`\0\x87Q` aU\xC9\x82\x85\x83\x8D\x01aT\\V[\x88Q\x91\x84\x01\x91aU\xDC\x81\x84\x84\x8D\x01aT\\V[\x88Q\x92\x01\x91aU\xEE\x81\x84\x84\x8C\x01aT\\V[\x87Q\x92\x01\x91aV\0\x81\x84\x84\x8B\x01aT\\V[\x86Q\x92\x01\x91aV\x12\x81\x84\x84\x8A\x01aT\\V[\x85Q\x92\x01\x91aV$\x81\x84\x84\x89\x01aT\\V[\x91\x90\x91\x01\x99\x98PPPPPPPPPV[`\0\x83QaVG\x81\x84` \x88\x01aT\\V[\x83Q\x90\x83\x01\x90aV[\x81\x83` \x88\x01aT\\V[\x01\x94\x93PPPPV[`\0\x87QaVv\x81\x84` \x8C\x01aT\\V[\x91\x90\x91\x01\x95\x86RP` \x85\x01\x93\x90\x93R`@\x84\x01\x91\x90\x91R``\x83\x01R`\x80\x82\x01R`\xA0\x01\x91\x90PV[`\0\x84QaV\xB2\x81\x84` \x89\x01aT\\V[\x84Q\x90\x83\x01\x90aV\xC6\x81\x83` \x89\x01aT\\V[\x84Q\x91\x01\x90aV\xD9\x81\x83` \x88\x01aT\\V[\x01\x95\x94PPPPPV[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82aW\x16WcNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[P\x06\x90V[`\0\x85QaW-\x81\x84` \x8A\x01aT\\V[\x85Q\x90\x83\x01\x90aWA\x81\x83` \x8A\x01aT\\V[\x85Q\x91\x01\x90aWT\x81\x83` \x89\x01aT\\V[\x84Q\x91\x01\x90aWg\x81\x83` \x88\x01aT\\V[\x01\x96\x95PPPPPPV[`\0\x89Q` aW\x85\x82\x85\x83\x8F\x01aT\\V[\x8AQ\x91\x84\x01\x91aW\x98\x81\x84\x84\x8F\x01aT\\V[\x8AQ\x92\x01\x91aW\xAA\x81\x84\x84\x8E\x01aT\\V[\x89Q\x92\x01\x91aW\xBC\x81\x84\x84\x8D\x01aT\\V[\x88Q\x92\x01\x91aW\xCE\x81\x84\x84\x8C\x01aT\\V[\x87Q\x92\x01\x91aW\xE0\x81\x84\x84\x8B\x01aT\\V[\x86Q\x92\x01\x91aW\xF2\x81\x84\x84\x8A\x01aT\\V[\x85Q\x92\x01\x91aX\x04\x81\x84\x84\x89\x01aT\\V[\x91\x90\x91\x01\x9B\x9APPPPPPPPPPPV[`\0\x88Q` aX*\x82\x85\x83\x8E\x01aT\\V[\x89Q\x91\x84\x01\x91aX=\x81\x84\x84\x8E\x01aT\\V[\x89Q\x92\x01\x91aXO\x81\x84\x84\x8D\x01aT\\V[\x88Q\x92\x01\x91aXa\x81\x84\x84\x8C\x01aT\\V[\x87Q\x92\x01\x91aXs\x81\x84\x84\x8B\x01aT\\V[\x86Q\x92\x01\x91aX\x85\x81\x84\x84\x8A\x01aT\\V[\x85Q\x92\x01\x91aX\x97\x81\x84\x84\x89\x01aT\\V[\x91\x90\x91\x01\x9A\x99PPPPPPPPPPV[`\0\x8AQaX\xBB\x81\x84` \x8F\x01aT\\V[\x91\x90\x91\x01\x98\x89RP` \x88\x01\x96\x90\x96R`@\x87\x01\x94\x90\x94R``\x86\x01\x92\x90\x92R`\x80\x85\x01R`\xA0\x84\x01R`\xC0\x83\x01R`\xE0\x82\x01Ra\x01\0\x01\x91\x90PV\xFE0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\xA6l\xC9(\xB5\xED\xB8*\xF9\xBDI\x92)T\x15Z\xB7\xB0\x94&\x94\xBE\xA4\xCEDf\x1D\x9A\x876\xC6\x88.+\x91Ea\x03i\x8A\xDFW\xB7\x99\x96\x9D\xEA\x1C\x8Fs\x9D\xA5\xD8\xD4\r\xD3\xEB\x92\"\xDB|\x81\xE8\x816\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE3\x1E\xE6x\xA0G\nu\xA6\xEA\xA8\xFE\x83p`I\x8B\xA8(\xA3p;1\x1D\x0Fw\xF0\x10BJ\xFE\xB0%\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE4 B\xA5\x87\xA9\x0C\x18{\n\x08|\x03\xE2\x9C\x96\x8B\x95\x0B\x1D\xB2m\\\x82\xD6f\x90Zh\x95y\x0C\n/\x8D\xD1\xF1\xA7X ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([84, 100, 96, 133], ()) + .expect("method not found (this should never happen)") + } + ///Calls the contract's `getHotShotCommitment` (0x8584d23f) function + pub fn get_hot_shot_commitment( + &self, + hot_shot_block_height: ::ethers::core::types::U256, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([133, 132, 210, 63], hot_shot_block_height) + .expect("method not found (this should never happen)") + } + ///Calls the contract's `getStateUpdateBlockNumbersCount` (0x7053fc51) function + pub fn get_state_update_block_numbers_count( + &self, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([112, 83, 252, 81], ()) + .expect("method not found (this should never happen)") + } ///Calls the contract's `getVersion` (0x0d8e6e2c) function pub fn get_version(&self) -> ::ethers::contract::builders::ContractCall { self.0 .method_hash([13, 142, 110, 44], ()) .expect("method not found (this should never happen)") } + ///Calls the contract's `hotShotCommitments` (0xdb13b60a) function + pub fn hot_shot_commitments( + &self, + p0: ::ethers::core::types::U256, + ) -> ::ethers::contract::builders::ContractCall + { + self.0 + .method_hash([219, 19, 182, 10], p0) + .expect("method not found (this should never happen)") + } ///Calls the contract's `initialize` (0xa244d596) function pub fn initialize( &self, @@ -1092,6 +1286,16 @@ pub mod light_client { .method_hash([162, 68, 213, 150], (genesis, num_blocks_per_epoch, owner)) .expect("method not found (this should never happen)") } + ///Calls the contract's `lagOverEscapeHatchThreshold` (0xe0303301) function + pub fn lag_over_escape_hatch_threshold( + &self, + block_number: ::ethers::core::types::U256, + threshold: ::ethers::core::types::U256, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([224, 48, 51, 1], (block_number, threshold)) + .expect("method not found (this should never happen)") + } ///Calls the contract's `newFinalizedState` (0x409939b7) function pub fn new_finalized_state( &self, @@ -1147,6 +1351,15 @@ pub mod light_client { .method_hash([1, 63, 165, 252], prover) .expect("method not found (this should never happen)") } + ///Calls the contract's `stateUpdateBlockNumbers` (0xa51e6fea) function + pub fn state_update_block_numbers( + &self, + p0: ::ethers::core::types::U256, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([165, 30, 111, 234], p0) + .expect("method not found (this should never happen)") + } ///Calls the contract's `states` (0x7f17baad) function pub fn states( &self, @@ -1343,6 +1556,24 @@ pub mod light_client { )] #[etherror(name = "FailedInnerCall", abi = "FailedInnerCall()")] pub struct FailedInnerCall; + ///Custom Error type `InsufficientSnapshotHistory` with signature `InsufficientSnapshotHistory()` and selector `0xb0b43877` + #[derive( + Clone, + ::ethers::contract::EthError, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[etherror( + name = "InsufficientSnapshotHistory", + abi = "InsufficientSnapshotHistory()" + )] + pub struct InsufficientSnapshotHistory; ///Custom Error type `InvalidAddress` with signature `InvalidAddress()` and selector `0xe6c4247b` #[derive( Clone, @@ -1373,6 +1604,24 @@ pub mod light_client { )] #[etherror(name = "InvalidArgs", abi = "InvalidArgs()")] pub struct InvalidArgs; + ///Custom Error type `InvalidHotShotBlockForCommitmentCheck` with signature `InvalidHotShotBlockForCommitmentCheck()` and selector `0x615a9264` + #[derive( + Clone, + ::ethers::contract::EthError, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[etherror( + name = "InvalidHotShotBlockForCommitmentCheck", + abi = "InvalidHotShotBlockForCommitmentCheck()" + )] + pub struct InvalidHotShotBlockForCommitmentCheck; ///Custom Error type `InvalidInitialization` with signature `InvalidInitialization()` and selector `0xf92ee8a9` #[derive( Clone, @@ -1649,8 +1898,10 @@ pub mod light_client { ERC1967InvalidImplementation(ERC1967InvalidImplementation), ERC1967NonPayable(ERC1967NonPayable), FailedInnerCall(FailedInnerCall), + InsufficientSnapshotHistory(InsufficientSnapshotHistory), InvalidAddress(InvalidAddress), InvalidArgs(InvalidArgs), + InvalidHotShotBlockForCommitmentCheck(InvalidHotShotBlockForCommitmentCheck), InvalidInitialization(InvalidInitialization), InvalidPolyEvalArgs(InvalidPolyEvalArgs), InvalidProof(InvalidProof), @@ -1697,12 +1948,24 @@ pub mod light_client { if let Ok(decoded) = ::decode(data) { return Ok(Self::FailedInnerCall(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::InsufficientSnapshotHistory(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::InvalidAddress(decoded)); } if let Ok(decoded) = ::decode(data) { return Ok(Self::InvalidArgs(decoded)); } + if let Ok(decoded) = + ::decode( + data, + ) + { + return Ok(Self::InvalidHotShotBlockForCommitmentCheck(decoded)); + } if let Ok(decoded) = ::decode(data) { @@ -1785,8 +2048,14 @@ pub mod light_client { } Self::ERC1967NonPayable(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::FailedInnerCall(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::InsufficientSnapshotHistory(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::InvalidAddress(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::InvalidArgs(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::InvalidHotShotBlockForCommitmentCheck(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::InvalidInitialization(element) => { ::ethers::core::abi::AbiEncode::encode(element) } @@ -1847,12 +2116,20 @@ pub mod light_client { == ::selector() => { true } + _ if selector + == ::selector() => { + true + } _ if selector == ::selector() => { true } _ if selector == ::selector() => true, + _ if selector + == ::selector() => { + true + } _ if selector == ::selector() => { true @@ -1926,8 +2203,12 @@ pub mod light_client { } Self::ERC1967NonPayable(element) => ::core::fmt::Display::fmt(element, f), Self::FailedInnerCall(element) => ::core::fmt::Display::fmt(element, f), + Self::InsufficientSnapshotHistory(element) => ::core::fmt::Display::fmt(element, f), Self::InvalidAddress(element) => ::core::fmt::Display::fmt(element, f), Self::InvalidArgs(element) => ::core::fmt::Display::fmt(element, f), + Self::InvalidHotShotBlockForCommitmentCheck(element) => { + ::core::fmt::Display::fmt(element, f) + } Self::InvalidInitialization(element) => ::core::fmt::Display::fmt(element, f), Self::InvalidPolyEvalArgs(element) => ::core::fmt::Display::fmt(element, f), Self::InvalidProof(element) => ::core::fmt::Display::fmt(element, f), @@ -1977,6 +2258,11 @@ pub mod light_client { Self::FailedInnerCall(value) } } + impl ::core::convert::From for LightClientErrors { + fn from(value: InsufficientSnapshotHistory) -> Self { + Self::InsufficientSnapshotHistory(value) + } + } impl ::core::convert::From for LightClientErrors { fn from(value: InvalidAddress) -> Self { Self::InvalidAddress(value) @@ -1987,6 +2273,11 @@ pub mod light_client { Self::InvalidArgs(value) } } + impl ::core::convert::From for LightClientErrors { + fn from(value: InvalidHotShotBlockForCommitmentCheck) -> Self { + Self::InvalidHotShotBlockForCommitmentCheck(value) + } + } impl ::core::convert::From for LightClientErrors { fn from(value: InvalidInitialization) -> Self { Self::InvalidInitialization(value) @@ -2469,6 +2760,59 @@ pub mod light_client { )] #[ethcall(name = "getGenesisState", abi = "getGenesisState()")] pub struct GetGenesisStateCall; + ///Container type for all input parameters for the `getHotShotBlockCommitmentsCount` function with signature `getHotShotBlockCommitmentsCount()` and selector `0x54646085` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "getHotShotBlockCommitmentsCount", + abi = "getHotShotBlockCommitmentsCount()" + )] + pub struct GetHotShotBlockCommitmentsCountCall; + ///Container type for all input parameters for the `getHotShotCommitment` function with signature `getHotShotCommitment(uint256)` and selector `0x8584d23f` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall(name = "getHotShotCommitment", abi = "getHotShotCommitment(uint256)")] + pub struct GetHotShotCommitmentCall { + pub hot_shot_block_height: ::ethers::core::types::U256, + } + ///Container type for all input parameters for the `getStateUpdateBlockNumbersCount` function with signature `getStateUpdateBlockNumbersCount()` and selector `0x7053fc51` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "getStateUpdateBlockNumbersCount", + abi = "getStateUpdateBlockNumbersCount()" + )] + pub struct GetStateUpdateBlockNumbersCountCall; ///Container type for all input parameters for the `getVersion` function with signature `getVersion()` and selector `0x0d8e6e2c` #[derive( Clone, @@ -2484,6 +2828,21 @@ pub mod light_client { )] #[ethcall(name = "getVersion", abi = "getVersion()")] pub struct GetVersionCall; + ///Container type for all input parameters for the `hotShotCommitments` function with signature `hotShotCommitments(uint256)` and selector `0xdb13b60a` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall(name = "hotShotCommitments", abi = "hotShotCommitments(uint256)")] + pub struct HotShotCommitmentsCall(pub ::ethers::core::types::U256); ///Container type for all input parameters for the `initialize` function with signature `initialize((uint64,uint64,uint256,uint256,uint256,uint256,uint256,uint256),uint32,address)` and selector `0xa244d596` #[derive( Clone, @@ -2506,6 +2865,27 @@ pub mod light_client { pub num_blocks_per_epoch: u32, pub owner: ::ethers::core::types::Address, } + ///Container type for all input parameters for the `lagOverEscapeHatchThreshold` function with signature `lagOverEscapeHatchThreshold(uint256,uint256)` and selector `0xe0303301` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "lagOverEscapeHatchThreshold", + abi = "lagOverEscapeHatchThreshold(uint256,uint256)" + )] + pub struct LagOverEscapeHatchThresholdCall { + pub block_number: ::ethers::core::types::U256, + pub threshold: ::ethers::core::types::U256, + } ///Container type for all input parameters for the `newFinalizedState` function with signature `newFinalizedState((uint64,uint64,uint256,uint256,uint256,uint256,uint256,uint256),((uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256))` and selector `0x409939b7` #[derive( Clone, @@ -2617,6 +2997,24 @@ pub mod light_client { pub struct SetPermissionedProverCall { pub prover: ::ethers::core::types::Address, } + ///Container type for all input parameters for the `stateUpdateBlockNumbers` function with signature `stateUpdateBlockNumbers(uint256)` and selector `0xa51e6fea` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "stateUpdateBlockNumbers", + abi = "stateUpdateBlockNumbers(uint256)" + )] + pub struct StateUpdateBlockNumbersCall(pub ::ethers::core::types::U256); ///Container type for all input parameters for the `states` function with signature `states(uint32)` and selector `0x7f17baad` #[derive( Clone, @@ -2714,8 +3112,13 @@ pub mod light_client { FrozenThreshold(FrozenThresholdCall), GetFinalizedState(GetFinalizedStateCall), GetGenesisState(GetGenesisStateCall), + GetHotShotBlockCommitmentsCount(GetHotShotBlockCommitmentsCountCall), + GetHotShotCommitment(GetHotShotCommitmentCall), + GetStateUpdateBlockNumbersCount(GetStateUpdateBlockNumbersCountCall), GetVersion(GetVersionCall), + HotShotCommitments(HotShotCommitmentsCall), Initialize(InitializeCall), + LagOverEscapeHatchThreshold(LagOverEscapeHatchThresholdCall), NewFinalizedState(NewFinalizedStateCall), Owner(OwnerCall), PermissionedProver(PermissionedProverCall), @@ -2723,6 +3126,7 @@ pub mod light_client { ProxiableUUID(ProxiableUUIDCall), RenounceOwnership(RenounceOwnershipCall), SetPermissionedProver(SetPermissionedProverCall), + StateUpdateBlockNumbers(StateUpdateBlockNumbersCall), States(StatesCall), TransferOwnership(TransferOwnershipCall), UpgradeToAndCall(UpgradeToAndCallCall), @@ -2778,12 +3182,41 @@ pub mod light_client { { return Ok(Self::GetGenesisState(decoded)); } + if let Ok(decoded) = + ::decode( + data, + ) + { + return Ok(Self::GetHotShotBlockCommitmentsCount(decoded)); + } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::GetHotShotCommitment(decoded)); + } + if let Ok(decoded) = + ::decode( + data, + ) + { + return Ok(Self::GetStateUpdateBlockNumbersCount(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::GetVersion(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::HotShotCommitments(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::Initialize(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::LagOverEscapeHatchThreshold(decoded)); + } if let Ok(decoded) = ::decode(data) { @@ -2816,6 +3249,11 @@ pub mod light_client { { return Ok(Self::SetPermissionedProver(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::StateUpdateBlockNumbers(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::States(decoded)); } @@ -2862,8 +3300,23 @@ pub mod light_client { Self::FrozenThreshold(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::GetFinalizedState(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::GetGenesisState(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::GetHotShotBlockCommitmentsCount(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } + Self::GetHotShotCommitment(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } + Self::GetStateUpdateBlockNumbersCount(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::GetVersion(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::HotShotCommitments(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::Initialize(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::LagOverEscapeHatchThreshold(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::NewFinalizedState(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::Owner(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::PermissionedProver(element) => { @@ -2877,6 +3330,9 @@ pub mod light_client { Self::SetPermissionedProver(element) => { ::ethers::core::abi::AbiEncode::encode(element) } + Self::StateUpdateBlockNumbers(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::States(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::TransferOwnership(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::UpgradeToAndCall(element) => ::ethers::core::abi::AbiEncode::encode(element), @@ -2901,8 +3357,17 @@ pub mod light_client { Self::FrozenThreshold(element) => ::core::fmt::Display::fmt(element, f), Self::GetFinalizedState(element) => ::core::fmt::Display::fmt(element, f), Self::GetGenesisState(element) => ::core::fmt::Display::fmt(element, f), + Self::GetHotShotBlockCommitmentsCount(element) => { + ::core::fmt::Display::fmt(element, f) + } + Self::GetHotShotCommitment(element) => ::core::fmt::Display::fmt(element, f), + Self::GetStateUpdateBlockNumbersCount(element) => { + ::core::fmt::Display::fmt(element, f) + } Self::GetVersion(element) => ::core::fmt::Display::fmt(element, f), + Self::HotShotCommitments(element) => ::core::fmt::Display::fmt(element, f), Self::Initialize(element) => ::core::fmt::Display::fmt(element, f), + Self::LagOverEscapeHatchThreshold(element) => ::core::fmt::Display::fmt(element, f), Self::NewFinalizedState(element) => ::core::fmt::Display::fmt(element, f), Self::Owner(element) => ::core::fmt::Display::fmt(element, f), Self::PermissionedProver(element) => ::core::fmt::Display::fmt(element, f), @@ -2910,6 +3375,7 @@ pub mod light_client { Self::ProxiableUUID(element) => ::core::fmt::Display::fmt(element, f), Self::RenounceOwnership(element) => ::core::fmt::Display::fmt(element, f), Self::SetPermissionedProver(element) => ::core::fmt::Display::fmt(element, f), + Self::StateUpdateBlockNumbers(element) => ::core::fmt::Display::fmt(element, f), Self::States(element) => ::core::fmt::Display::fmt(element, f), Self::TransferOwnership(element) => ::core::fmt::Display::fmt(element, f), Self::UpgradeToAndCall(element) => ::core::fmt::Display::fmt(element, f), @@ -2963,16 +3429,41 @@ pub mod light_client { Self::GetGenesisState(value) } } + impl ::core::convert::From for LightClientCalls { + fn from(value: GetHotShotBlockCommitmentsCountCall) -> Self { + Self::GetHotShotBlockCommitmentsCount(value) + } + } + impl ::core::convert::From for LightClientCalls { + fn from(value: GetHotShotCommitmentCall) -> Self { + Self::GetHotShotCommitment(value) + } + } + impl ::core::convert::From for LightClientCalls { + fn from(value: GetStateUpdateBlockNumbersCountCall) -> Self { + Self::GetStateUpdateBlockNumbersCount(value) + } + } impl ::core::convert::From for LightClientCalls { fn from(value: GetVersionCall) -> Self { Self::GetVersion(value) } } + impl ::core::convert::From for LightClientCalls { + fn from(value: HotShotCommitmentsCall) -> Self { + Self::HotShotCommitments(value) + } + } impl ::core::convert::From for LightClientCalls { fn from(value: InitializeCall) -> Self { Self::Initialize(value) } } + impl ::core::convert::From for LightClientCalls { + fn from(value: LagOverEscapeHatchThresholdCall) -> Self { + Self::LagOverEscapeHatchThreshold(value) + } + } impl ::core::convert::From for LightClientCalls { fn from(value: NewFinalizedStateCall) -> Self { Self::NewFinalizedState(value) @@ -3008,6 +3499,11 @@ pub mod light_client { Self::SetPermissionedProver(value) } } + impl ::core::convert::From for LightClientCalls { + fn from(value: StateUpdateBlockNumbersCall) -> Self { + Self::StateUpdateBlockNumbers(value) + } + } impl ::core::convert::From for LightClientCalls { fn from(value: StatesCall) -> Self { Self::States(value) @@ -3145,6 +3641,48 @@ pub mod light_client { Hash, )] pub struct GetGenesisStateReturn(pub LightClientState); + ///Container type for all return fields from the `getHotShotBlockCommitmentsCount` function with signature `getHotShotBlockCommitmentsCount()` and selector `0x54646085` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct GetHotShotBlockCommitmentsCountReturn(pub ::ethers::core::types::U256); + ///Container type for all return fields from the `getHotShotCommitment` function with signature `getHotShotCommitment(uint256)` and selector `0x8584d23f` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct GetHotShotCommitmentReturn(pub HotShotCommitment); + ///Container type for all return fields from the `getStateUpdateBlockNumbersCount` function with signature `getStateUpdateBlockNumbersCount()` and selector `0x7053fc51` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct GetStateUpdateBlockNumbersCountReturn(pub ::ethers::core::types::U256); ///Container type for all return fields from the `getVersion` function with signature `getVersion()` and selector `0x0d8e6e2c` #[derive( Clone, @@ -3163,6 +3701,37 @@ pub mod light_client { pub minor_version: u8, pub patch_version: u8, } + ///Container type for all return fields from the `hotShotCommitments` function with signature `hotShotCommitments(uint256)` and selector `0xdb13b60a` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct HotShotCommitmentsReturn { + pub block_height: u64, + pub block_comm_root: ::ethers::core::types::U256, + } + ///Container type for all return fields from the `lagOverEscapeHatchThreshold` function with signature `lagOverEscapeHatchThreshold(uint256,uint256)` and selector `0xe0303301` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct LagOverEscapeHatchThresholdReturn(pub bool); ///Container type for all return fields from the `owner` function with signature `owner()` and selector `0x8da5cb5b` #[derive( Clone, @@ -3219,6 +3788,20 @@ pub mod light_client { Hash, )] pub struct ProxiableUUIDReturn(pub [u8; 32]); + ///Container type for all return fields from the `stateUpdateBlockNumbers` function with signature `stateUpdateBlockNumbers(uint256)` and selector `0xa51e6fea` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct StateUpdateBlockNumbersReturn(pub ::ethers::core::types::U256); ///Container type for all return fields from the `states` function with signature `states(uint32)` and selector `0x7f17baad` #[derive( Clone, diff --git a/contract-bindings/src/light_client_mock.rs b/contract-bindings/src/light_client_mock.rs index c8acbe2fa..d198c1973 100644 --- a/contract-bindings/src/light_client_mock.rs +++ b/contract-bindings/src/light_client_mock.rs @@ -219,6 +219,65 @@ pub mod light_client_mock { state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, },], ), + ( + ::std::borrow::ToOwned::to_owned("getHotShotBlockCommitmentsCount"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("getHotShotBlockCommitmentsCount",), + inputs: ::std::vec![], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), + ( + ::std::borrow::ToOwned::to_owned("getHotShotCommitment"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("getHotShotCommitment",), + inputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("hotShotBlockHeight",), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Tuple(::std::vec![ + ::ethers::core::abi::ethabi::ParamType::Uint(64usize), + ::ethers::core::abi::ethabi::ParamType::Uint(256usize), + ],), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned( + "struct LightClient.HotShotCommitment", + ), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), + ( + ::std::borrow::ToOwned::to_owned("getStateUpdateBlockNumbersCount"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("getStateUpdateBlockNumbersCount",), + inputs: ::std::vec![], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), ( ::std::borrow::ToOwned::to_owned("getVersion"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -251,6 +310,37 @@ pub mod light_client_mock { state_mutability: ::ethers::core::abi::ethabi::StateMutability::Pure, },], ), + ( + ::std::borrow::ToOwned::to_owned("hotShotCommitments"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("hotShotCommitments"), + inputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + outputs: ::std::vec![ + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("blockHeight"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(64usize), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint64"), + ), + }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("blockCommRoot"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("BN254.ScalarField"), + ), + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), ( ::std::borrow::ToOwned::to_owned("initialize"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -294,6 +384,37 @@ pub mod light_client_mock { state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, },], ), + ( + ::std::borrow::ToOwned::to_owned("lagOverEscapeHatchThreshold"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("lagOverEscapeHatchThreshold",), + inputs: ::std::vec![ + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("blockNumber"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("threshold"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + }, + ], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Bool, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("bool"), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), ( ::std::borrow::ToOwned::to_owned("newFinalizedState"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -512,6 +633,31 @@ pub mod light_client_mock { state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, },], ), + ( + ::std::borrow::ToOwned::to_owned("setHotShotCommitments"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("setHotShotCommitments",), + inputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("values"), + kind: ::ethers::core::abi::ethabi::ParamType::Array( + ::std::boxed::Box::new( + ::ethers::core::abi::ethabi::ParamType::Tuple(::std::vec![ + ::ethers::core::abi::ethabi::ParamType::Uint(64usize), + ::ethers::core::abi::ethabi::ParamType::Uint(256usize), + ],), + ), + ), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned( + "struct LightClient.HotShotCommitment[]", + ), + ), + },], + outputs: ::std::vec![], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, + },], + ), ( ::std::borrow::ToOwned::to_owned("setPermissionedProver"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -528,6 +674,48 @@ pub mod light_client_mock { state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, },], ), + ( + ::std::borrow::ToOwned::to_owned("setStateUpdateBlockNumbers"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("setStateUpdateBlockNumbers",), + inputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("values"), + kind: ::ethers::core::abi::ethabi::ParamType::Array( + ::std::boxed::Box::new( + ::ethers::core::abi::ethabi::ParamType::Uint(256usize), + ), + ), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256[]"), + ), + },], + outputs: ::std::vec![], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, + },], + ), + ( + ::std::borrow::ToOwned::to_owned("stateUpdateBlockNumbers"), + ::std::vec![::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("stateUpdateBlockNumbers",), + inputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + outputs: ::std::vec![::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("uint256"), + ), + },], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + },], + ), ( ::std::borrow::ToOwned::to_owned("states"), ::std::vec![::ethers::core::abi::ethabi::Function { @@ -829,6 +1017,13 @@ pub mod light_client_mock { inputs: ::std::vec![], },], ), + ( + ::std::borrow::ToOwned::to_owned("InsufficientSnapshotHistory"), + ::std::vec![::ethers::core::abi::ethabi::AbiError { + name: ::std::borrow::ToOwned::to_owned("InsufficientSnapshotHistory",), + inputs: ::std::vec![], + },], + ), ( ::std::borrow::ToOwned::to_owned("InvalidAddress"), ::std::vec![::ethers::core::abi::ethabi::AbiError { @@ -843,6 +1038,15 @@ pub mod light_client_mock { inputs: ::std::vec![], },], ), + ( + ::std::borrow::ToOwned::to_owned("InvalidHotShotBlockForCommitmentCheck"), + ::std::vec![::ethers::core::abi::ethabi::AbiError { + name: ::std::borrow::ToOwned::to_owned( + "InvalidHotShotBlockForCommitmentCheck", + ), + inputs: ::std::vec![], + },], + ), ( ::std::borrow::ToOwned::to_owned("InvalidInitialization"), ::std::vec![::ethers::core::abi::ethabi::AbiError { @@ -988,12 +1192,12 @@ pub mod light_client_mock { pub static LIGHTCLIENTMOCK_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\xA0`@R0`\x80R4\x80\x15b\0\0\x15W`\0\x80\xFD[P`@Qb\0c\xCA8\x03\x80b\0c\xCA\x839\x81\x01`@\x81\x90Rb\0\08\x91b\0\x04\x1BV[b\0\0Bb\0\0VV[b\0\0N\x82\x82b\0\x01\nV[PPb\0\x04\xEEV[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15b\0\0\xA7W`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80T`\x01`\x01`@\x1B\x03\x90\x81\x16\x14b\0\x01\x07W\x80T`\x01`\x01`@\x1B\x03\x19\x16`\x01`\x01`@\x1B\x03\x90\x81\x17\x82U`@Q\x90\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PV[\x81Q`\x01`\x01`@\x1B\x03\x16\x15\x15\x80b\0\x01/WP` \x82\x01Q`\x01`\x01`@\x1B\x03\x16\x15\x15[\x80b\0\x01=WP`\x80\x82\x01Q\x15[\x80b\0\x01KWP`\xA0\x82\x01Q\x15[\x80b\0\x01YWP`\xC0\x82\x01Q\x15[\x80b\0\x01gWP`\xE0\x82\x01Q\x15[\x80b\0\x01wWPc\xFF\xFF\xFF\xFF\x81\x16\x15[\x15b\0\x01\x96W`@QcP\xDD\x03\xF7`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x81`\x05`\0\x80`\x04\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP\x81`\x05`\0\x80`\x08\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP`\0\x80`\x0Ca\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP\x80`\0\x80a\x01\0\n\x81T\x81c\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83c\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`\0b\0\x03\x84\x83b\0\x03\xA1` \x1B` \x1CV[`\x01\x81\x90U`\xE0\x90\x93\x01Q`\x02\x81\x90U`\x03\x93\x90\x93UPP`\x04UV[`\x80\x80\x82\x01Q`\xA0\x83\x01Q`\xC0\x84\x01Q`@\x80Q` \x81\x01\x94\x90\x94R\x83\x01\x91\x90\x91R``\x82\x01R`\0\x91\x01`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[\x80Q`\x01`\x01`@\x1B\x03\x81\x16\x81\x14b\0\x04\x01W`\0\x80\xFD[\x91\x90PV[\x80Qc\xFF\xFF\xFF\xFF\x81\x16\x81\x14b\0\x04\x01W`\0\x80\xFD[`\0\x80\x82\x84\x03a\x01 \x81\x12\x15b\0\x041W`\0\x80\xFD[a\x01\0\x80\x82\x12\x15b\0\x04BW`\0\x80\xFD[`@Q\x91P\x80\x82\x01`\x01`\x01`@\x1B\x03\x81\x11\x83\x82\x10\x17\x15b\0\x04tWcNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Rb\0\x04\x82\x85b\0\x03\xE9V[\x82Rb\0\x04\x92` \x86\x01b\0\x03\xE9V[` \x83\x01R`@\x85\x01Q`@\x83\x01R``\x85\x01Q``\x83\x01R`\x80\x85\x01Q`\x80\x83\x01R`\xA0\x85\x01Q`\xA0\x83\x01R`\xC0\x85\x01Q`\xC0\x83\x01R`\xE0\x85\x01Q`\xE0\x83\x01R\x81\x93Pb\0\x04\xE3\x81\x86\x01b\0\x04\x06V[\x92PPP\x92P\x92\x90PV[`\x80Qa^\xB2b\0\x05\x18`\09`\0\x81\x81a\x14\xD2\x01R\x81\x81a\x14\xFB\x01Ra\x16\xE9\x01Ra^\xB2`\0\xF3\xFE`\x80`@R`\x046\x10a\x01\x96W`\x005`\xE0\x1C\x80cqP\x18\xA6\x11a\0\xE1W\x80c\xA2D\xD5\x96\x11a\0\x8AW\x80c\xBD2Q\x9A\x11a\0dW\x80c\xBD2Q\x9A\x14a\x06\xBFW\x80c\xCAo\xE8U\x14a\x07\x01W\x80c\xF0h T\x14a\x07\x17W\x80c\xF2\xFD\xE3\x8B\x14a\x07IW`\0\x80\xFD[\x80c\xA2D\xD5\x96\x14a\x05\xE6W\x80c\xAA\x92'2\x14a\x06\x06W\x80c\xAD<\xB1\xCC\x14a\x06iW`\0\x80\xFD[\x80c\x7F\x17\xBA\xAD\x11a\0\xBBW\x80c\x7F\x17\xBA\xAD\x14a\x04\xDFW\x80c\x82\xD0\x7F\xF3\x14a\x05\x94W\x80c\x8D\xA5\xCB[\x14a\x05\xA9W`\0\x80\xFD[\x80cqP\x18\xA6\x14a\x04jW\x80cvg\x18\x08\x14a\x04\x7FW\x80cv\xB6\xB7\xCB\x14a\x04\xC9W`\0\x80\xFD[\x80c@\x999\xB7\x11a\x01CW\x80cR\xD1\x90-\x11a\x01\x1DW\x80cR\xD1\x90-\x14a\x04*W\x80cb\x82w3\x14a\x04?W\x80ci\xCCj\x04\x14a\x04UW`\0\x80\xFD[\x80c@\x999\xB7\x14a\x03sW\x80cHG\xAE]\x14a\x03\x93W\x80cO\x1E\xF2\x86\x14a\x04\x17W`\0\x80\xFD[\x80c1=\xF7\xB1\x11a\x01tW\x80c1=\xF7\xB1\x14a\x02\xB1W\x80c8+!Z\x14a\x02\xE9W\x80c9I\xD1\xE9\x14a\x03\rW`\0\x80\xFD[\x80c\x01?\xA5\xFC\x14a\x01\x9BW\x80c\r\x8En,\x14a\x01\xBDW\x80c *\n\xDB\x14a\x01\xEFW[`\0\x80\xFD[4\x80\x15a\x01\xA7W`\0\x80\xFD[Pa\x01\xBBa\x01\xB66`\x04aU\x83V[a\x07iV[\0[4\x80\x15a\x01\xC9W`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\xFBW`\0\x80\xFD[Pa\x01\xBBa\x02\n6`\x04aV\xC5V[`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x85Q\x81T\x92\x87\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x81\x16\x90\x95\x02\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x93\x16\x94\x16\x93\x90\x93\x17\x17\x82U\x91\x83\x01Q`\x01\x82\x01U``\x83\x01Q`\x02\x82\x01U`\x80\x83\x01Q`\x03\x82\x01U`\xA0\x83\x01Q`\x04\x82\x01U`\xC0\x83\x01Q\x91\x81\x01\x91\x90\x91U`\xE0\x90\x91\x01Q`\x06\x90\x91\x01UV[4\x80\x15a\x02\xBDW`\0\x80\xFD[P`\x06Ta\x02\xD1\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE6V[4\x80\x15a\x02\xF5W`\0\x80\xFD[Pa\x02\xFF`\x03T\x81V[`@Q\x90\x81R` \x01a\x01\xE6V[4\x80\x15a\x03\x19W`\0\x80\xFD[Pa\x01\xBBa\x03(6`\x04aV\xE2V[`\0\x80Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x92\x16l\x01\0\0\0\0\0\0\0\0\0\0\0\0\x02\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x92\x16\x91\x90\x91\x17\x90UV[4\x80\x15a\x03\x7FW`\0\x80\xFD[Pa\x01\xBBa\x03\x8E6`\x04aWLV[a\x08\x81V[4\x80\x15a\x03\x9FW`\0\x80\xFD[Pa\x03\xA8a\x0B\xB9V[`@Qa\x01\xE6\x91\x90`\0a\x01\0\x82\x01\x90Pg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x84Q\x16\x83R\x80` \x85\x01Q\x16` \x84\x01RP`@\x83\x01Q`@\x83\x01R``\x83\x01Q``\x83\x01R`\x80\x83\x01Q`\x80\x83\x01R`\xA0\x83\x01Q`\xA0\x83\x01R`\xC0\x83\x01Q`\xC0\x83\x01R`\xE0\x83\x01Q`\xE0\x83\x01R\x92\x91PPV[a\x01\xBBa\x04%6`\x04aY$V[a\x0C\xA1V[4\x80\x15a\x046W`\0\x80\xFD[Pa\x02\xFFa\x0C\xC0V[4\x80\x15a\x04KW`\0\x80\xFD[Pa\x02\xFF`\x02T\x81V[4\x80\x15a\x04aW`\0\x80\xFD[Pa\x01\xBBa\x0C\xEFV[4\x80\x15a\x04vW`\0\x80\xFD[Pa\x01\xBBa\r\xA1V[4\x80\x15a\x04\x8BW`\0\x80\xFD[P`\0Ta\x04\xB0\x90l\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81V[`@Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x91\x16\x81R` \x01a\x01\xE6V[4\x80\x15a\x04\xD5W`\0\x80\xFD[Pa\x02\xFF`\x01T\x81V[4\x80\x15a\x04\xEBW`\0\x80\xFD[Pa\x05Ma\x04\xFA6`\x04aY\xDEV[`\x05` \x81\x90R`\0\x91\x82R`@\x90\x91 \x80T`\x01\x82\x01T`\x02\x83\x01T`\x03\x84\x01T`\x04\x85\x01T\x95\x85\x01T`\x06\x90\x95\x01Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x86\x16\x97`\x01`@\x1B\x90\x96\x04\x16\x95\x93\x94\x92\x93\x91\x92\x91\x90\x88V[`@\x80Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x99\x8A\x16\x81R\x98\x90\x97\x16` \x89\x01R\x95\x87\x01\x94\x90\x94R``\x86\x01\x92\x90\x92R`\x80\x85\x01R`\xA0\x84\x01R`\xC0\x83\x01R`\xE0\x82\x01Ra\x01\0\x01a\x01\xE6V[4\x80\x15a\x05\xA0W`\0\x80\xFD[Pa\x03\xA8a\r\xB3V[4\x80\x15a\x05\xB5W`\0\x80\xFD[P\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`\x01`\x01`\xA0\x1B\x03\x16a\x02\xD1V[4\x80\x15a\x05\xF2W`\0\x80\xFD[Pa\x01\xBBa\x06\x016`\x04aY\xF9V[a\x0E\x98V[4\x80\x15a\x06\x12W`\0\x80\xFD[Pa\x02\xFFa\x06!6`\x04aV\xC5V[`\x80\x80\x82\x01Q`\xA0\x83\x01Q`\xC0\x84\x01Q`@\x80Q` \x81\x01\x94\x90\x94R\x83\x01\x91\x90\x91R``\x82\x01R`\0\x91\x01`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[4\x80\x15a\x06uW`\0\x80\xFD[Pa\x06\xB2`@Q\x80`@\x01`@R\x80`\x05\x81R` \x01\x7F5.0.0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81RP\x81V[`@Qa\x01\xE6\x91\x90aZdV[4\x80\x15a\x06\xCBW`\0\x80\xFD[P`\x06Ta\x06\xF1\x90t\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x81V[`@Q\x90\x15\x15\x81R` \x01a\x01\xE6V[4\x80\x15a\x07\rW`\0\x80\xFD[Pa\x02\xFF`\x04T\x81V[4\x80\x15a\x07#W`\0\x80\xFD[P`\0Ta\x074\x90c\xFF\xFF\xFF\xFF\x16\x81V[`@Qc\xFF\xFF\xFF\xFF\x90\x91\x16\x81R` \x01a\x01\xE6V[4\x80\x15a\x07UW`\0\x80\xFD[Pa\x01\xBBa\x07d6`\x04aU\x83V[a\x10$V[a\x07qa\x10{V[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x07\xB1W`@Q\x7F\xE6\xC4${\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x06T`\x01`\x01`\xA0\x1B\x03\x90\x81\x16\x90\x82\x16\x03a\x07\xF9W`@Q\x7F\xA8c\xAE\xC9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x06\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16`\x01`\x01`\xA0\x1B\x03\x83\x81\x16\x91\x90\x91\x17t\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x17\x91\x82\x90U`@Q\x91\x16\x81R\x7F\x80\x17\xBB\x88\x7F\xDF\x8F\xCAC\x14\xA9\xD4\x0Fns\xB3\xB8\x10\x02\xD6~\\\xFA\x85\xD8\x81s\xAFj\xA4`r\x90` \x01[`@Q\x80\x91\x03\x90\xA1PV[`\x06Tt\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x80\x15a\x08\xB6WP`\x06T`\x01`\x01`\xA0\x1B\x03\x163\x14\x15[\x15a\t/W`\x06T`\x01`\x01`\xA0\x1B\x03\x16a\x08\xFDW`@Q\x7F%\xCD\xA3\xCE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`@Q\x7F\xA3\xA6G\x80\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\t7a\r\xB3V[Q\x82Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x91\x82\x16\x91\x16\x11\x15\x80a\txWPa\tXa\r\xB3V[` \x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x82` \x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x11\x15[\x15a\t\xAFW`@Q\x7F\x05\x1CF\xEF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0\x80Ta\t\xDE\x90c\xFF\xFF\xFF\xFF\x81\x16\x90l\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16aZ\xADV[`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` R`@\x90\x91 T\x91\x92P\x90\x04g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x81\x16\x90\x82\x16\x14\x80\x15\x81a\n7WP\x81g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84` \x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x11[\x15a\n\x7FW`@Q\x7F\x1B#5\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x83\x16`\x04\x82\x01R`$\x01[`@Q\x80\x91\x03\x90\xFD[a\n\x8C\x84`@\x01Qa\x10\xEFV[a\n\x99\x84``\x01Qa\x10\xEFV[a\n\xA6\x84`\x80\x01Qa\x10\xEFV[a\n\xB3\x84`\xA0\x01Qa\x10\xEFV[a\n\xC0\x84`\xC0\x01Qa\x10\xEFV[\x80\x15a\n\xCEWa\n\xCEa\x11_V[a\n\xD8\x84\x84a\x12\xBCV[`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x88Q\x81T\x8A\x84\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x81\x16\x96\x87\x02\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x92\x16\x92\x16\x91\x82\x17\x17\x82U\x89\x86\x01Q`\x01\x83\x01\x81\x90U``\x8B\x01Q`\x02\x84\x01U`\x80\x8B\x01Q`\x03\x84\x01U`\xA0\x8B\x01Q`\x04\x84\x01U`\xC0\x8B\x01Q\x94\x83\x01\x94\x90\x94U`\xE0\x8A\x01Q`\x06\x90\x92\x01\x91\x90\x91U\x93Q\x91\x82R\x91\x92\x91\x7F\xA0Jw9$PZA\x85d67%\xF5h2\xF5w.k\x8D\r\xBDn\xFC\xE7$\xDF\xE8\x03\xDA\xE6\x91\x01`@Q\x80\x91\x03\x90\xA3PPPPV[a\x0C\x15`@Q\x80a\x01\0\x01`@R\x80`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[P`\0\x80Td\x01\0\0\0\0\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05` \x81\x81R`@\x92\x83\x90 \x83Qa\x01\0\x81\x01\x85R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R`\x01`@\x1B\x90\x91\x04\x16\x92\x81\x01\x92\x90\x92R`\x01\x81\x01T\x93\x82\x01\x93\x90\x93R`\x02\x83\x01T``\x82\x01R`\x03\x83\x01T`\x80\x82\x01R`\x04\x83\x01T`\xA0\x82\x01R\x90\x82\x01T`\xC0\x82\x01R`\x06\x90\x91\x01T`\xE0\x82\x01R\x90V[a\x0C\xA9a\x14\xC7V[a\x0C\xB2\x82a\x15\x97V[a\x0C\xBC\x82\x82a\x15\xD8V[PPV[`\0a\x0C\xCAa\x16\xDEV[P\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x90V[a\x0C\xF7a\x10{V[`\x06Tt\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15a\rmW`\x06\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90U`@Q\x7F\x9A_W\xDE\x85m\xD6h\xC5M\xD9^\\U\xDF\x93C!q\xCB\xCAI\xA8wmV \xEAY\xC0$P\x90`\0\x90\xA1V[`@Q\x7F\xA8c\xAE\xC9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[V[a\r\xA9a\x10{V[a\r\x9F`\0a\x17@V[a\x0E\x0F`@Q\x80a\x01\0\x01`@R\x80`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[P`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x84Qa\x01\0\x81\x01\x86R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R\x95\x90\x04\x90\x94\x16\x91\x84\x01\x91\x90\x91R`\x01\x81\x01T\x93\x83\x01\x93\x90\x93R`\x02\x83\x01T``\x83\x01R`\x03\x83\x01T`\x80\x83\x01R`\x04\x83\x01T`\xA0\x83\x01R\x82\x01T`\xC0\x82\x01R`\x06\x90\x91\x01T`\xE0\x82\x01R\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80T`\x01`@\x1B\x81\x04`\xFF\x16\x15\x90g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\0\x81\x15\x80\x15a\x0E\xDEWP\x82[\x90P`\0\x82g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\x01\x14\x80\x15a\x0E\xFBWP0;\x15[\x90P\x81\x15\x80\x15a\x0F\tWP\x80\x15[\x15a\x0F@W`@Q\x7F\xF9.\xE8\xA9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\x16`\x01\x17\x85U\x83\x15a\x0F\x86W\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16`\x01`@\x1B\x17\x85U[a\x0F\x8F\x86a\x17\xC9V[a\x0F\x97a\x17\xDAV[`\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\xFF\xFF\xFF\xFF\x16`\x01`@\x1B\x17\x90Ua\x0F\xCF\x88\x88a\x17\xE2V[\x83\x15a\x10\x1AW\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16\x85U`@Q`\x01\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPPPV[a\x10,a\x10{V[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x10oW`@Q\x7F\x1EO\xBD\xF7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\0`\x04\x82\x01R`$\x01a\nvV[a\x10x\x81a\x17@V[PV[3a\x10\xAD\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\r\x9FW`@Q\x7F\x11\x8C\xDA\xA7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R3`\x04\x82\x01R`$\x01a\nvV[\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x81\x10\x80a\x0C\xBCW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1B`$\x82\x01R\x7FBn254: invalid scalar field\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[`\0\x80T`\x01`@\x1B\x80\x82\x04c\xFF\xFF\xFF\xFF\x16\x80\x84R`\x05` \x81\x81R`@\x80\x87 \x81Qa\x01\0\x81\x01\x83R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R\x97\x90\x04\x87\x16\x81\x85\x01R`\x01\x80\x83\x01T\x82\x85\x01R`\x02\x80\x84\x01T``\x80\x85\x01\x91\x90\x91R`\x03\x80\x86\x01T`\x80\x80\x87\x01\x82\x90R`\x04\x80\x89\x01T`\xA0\x89\x01\x81\x90R\x89\x8D\x01T`\xC0\x8A\x01\x81\x90R`\x06\x90\x9A\x01\x80T`\xE0\x90\x9A\x01\x99\x90\x99R\x8AQ\x80\x8D\x01\x94\x90\x94R\x83\x8B\x01R\x82\x85\x01\x98\x90\x98R\x88Q\x80\x83\x03\x90\x94\x01\x84R\x01\x90\x96R\x80Q\x90\x87\x01 \x85T\x83U\x94\x85\x90U\x83T\x90U\x95\x89R\x93\x90\x92R\x91T\x90U\x93\x90\x92\x90\x91`\x0C\x91a\x12T\x91\x85\x91l\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04\x16aZ\xD1V[\x82Ta\x01\0\x92\x90\x92\ng\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x81\x02\x19\x90\x93\x16\x91\x83\x16\x02\x17\x90\x91U`\0T`@Ql\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x91\x04\x90\x91\x16\x81R\x7F\xDB5X%\x9E\x03\x9D~P\xE8\x16\xB9\xDC\xCE0\xFB\x11M\x8A\x9C\x86\xEC\xA5\xAB\x14\xB6\x01\x94\xD6\x94]?\x91P` \x01a\x08vV[`\0a\x12\xC6a\x1A\xD2V[`@\x80Q`\x08\x80\x82Ra\x01 \x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x01\0\x806\x837\x01\x90PP\x90P`\x02T\x81`\0\x81Q\x81\x10a\x13\x05Wa\x13\x05aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x83`\0\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81`\x01\x81Q\x81\x10a\x133Wa\x133aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x83` \x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81`\x02\x81Q\x81\x10a\x13aWa\x13aaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x83`@\x01Q\x81`\x03\x81Q\x81\x10a\x13\x85Wa\x13\x85aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x83``\x01Q\x81`\x04\x81Q\x81\x10a\x13\xA9Wa\x13\xA9aZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 `\x03\x01T\x82Q\x90\x91\x83\x91\x81\x10a\x13\xEDWa\x13\xEDaZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x90\x91R`@\x90 `\x04\x01T\x81Q\x82\x90`\x06\x90\x81\x10a\x141Wa\x141aZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 \x01T\x81Q\x82\x90`\x07\x90\x81\x10a\x14tWa\x14taZ\xF2V[` \x02` \x01\x01\x81\x81RPPa\x14\x8B\x82\x82\x85a \xB3V[a\x14\xC1W`@Q\x7F\t\xBD\xE39\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[PPPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14\x80a\x15`WP\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16a\x15T\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBCT`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14\x15[\x15a\r\x9FW`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x15\x9Fa\x10{V[`@Q`\x01`\x01`\xA0\x1B\x03\x82\x16\x81R\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x90` \x01a\x08vV[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x162WP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x16/\x91\x81\x01\x90a[\x08V[`\x01[a\x16sW`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16`\x04\x82\x01R`$\x01a\nvV[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x81\x14a\x16\xCFW`@Q\x7F\xAA\x1DI\xA4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x81\x01\x82\x90R`$\x01a\nvV[a\x16\xD9\x83\x83a!\x9EV[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\r\x9FW`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x16`\x01`\x01`\xA0\x1B\x03\x84\x81\x16\x91\x82\x17\x84U`@Q\x92\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[a\x17\xD1a!\xF4V[a\x10x\x81a\"VV[a\r\x9Fa!\xF4V[\x81Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x15\x15\x80a\x18\x08WP` \x82\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x15\x15[\x80a\x18\x15WP`\x80\x82\x01Q\x15[\x80a\x18\"WP`\xA0\x82\x01Q\x15[\x80a\x18/WP`\xC0\x82\x01Q\x15[\x80a\x18!\x03<6\x8A\x93b-\xD4\x05\xB9\x05\xA0\xEB4L\x98\xB9\xD7\xCF\x08\xB0\xC5\xEB\xF7\xC89`\xA0\x82\x01QR~\x13y4*Mw\xD4p\x87C\xAF\xF0\x1F\xF2z\xA1\x19\x17G\x8F\xDC\x8E+}F0\x81sWr\xEA` `\xA0\x83\x01Q\x01R\x7F\x19M\xAF\x85\xD9\xEE\xD9\x93{(\xE2\xA6\x80\xFC\xC5\xA7i\"\xC1\\\xD3\x1D\xC4\xF6\0\xE1I9\xB8 \x0C\xE7`\xC0\x82\x01QR\x7F%(\x0B\x12F$\x91\x1C\x7F\x87\xB4\xC2\xD8\x7FY\xC6\xC0~>\xEE\xB1\raM\xA2\x16\xF6!\x9F\xFEP\xB6` `\xC0\x83\x01Q\x01R\x7F\x04\x88.\xF3\x98\x99\xEA8\xC9gzH\xB8\xF8\xCCjg(N\x17\xFF\x94\x02\x89\xFA\xAA5\x9E\xEC\x9B3\xA6`\xE0\x82\x01QR\x7F\x1B\xAE\x9F6\xE6\x19\x078\xC7\x11P\x1B\xE5?)\x9B\xF6\x13H\xE6\x1E.\xF9\xD5wv\x0Ed\xF6)6\x8D` `\xE0\x83\x01Q\x01R\x7F-\x81\r0\x12\x0C\xB9>\x1A%K\x89\xED\n\xE8lv\x1FI\xB4\xF1)E\x9C\xD54\xF9U\x18Q5\x0Fa\x01\0\x82\x01QR\x7F\x0B%9M\xA5\xA1\xD45\xDA\xCC\xC2\xEA\xDD\x03\x9E,'\t\xF5\xF4/\xAB\xD9\xAF\xBA\x81^\xD6-j\xF3k` a\x01\0\x83\x01Q\x01R\x7F\x1C,\xE7\xBEW\x0B\xEA\x9EC\xF3\xD3\xD7\xCB\xCA\x84\xBD\xB4\xFC\x89\xB5:\xE6WS\x1D\xE7&p\xA6\x10^\x08a\x01 \x82\x01QR\x7F\x1A\x02U\xEC\x8C|\x87i3[\xC9\xDCk\"*\xC6\xA0Nvm\x08\xB4\\\x8C\xC5sY,\x05\xBC\x9C\x14` a\x01 \x83\x01Q\x01R\x7F\x1C\x16AY\x13k\x8F[Gs\xE13\xF4\x83\xA8\xA1\x92\xAB\x15\xD6\xD3\xEE\x01/\x17\x1B=\x02\xFDE\x06\xE7a\x01@\x82\x01QR\x7F'.\xB7\xD63\xCE\xDBh\xCE\x01\x13\xF4B\n\xB5a\x0B\x81\xB8\xBA\x1A\xB94\x8D\xB1Wa\xD4\x0E\x8D\xF5\xBA` a\x01@\x83\x01Q\x01R\x7F\x0EDf9\xAAl\xAF%\xE9>\xF7G\x08N9\xB8\xEA\x90\xAB\xF2;\xB4\x8C(\xFD_\x9B\xA7\xBAeP\"a\x01`\x82\x01QR\x7F\x03>\x19Z\x9E\xA3\xA9\xCE@\xB7+g:\xFBEDL\xA1\xB1_\x05C\xF4M\x10\xF5\xC6@\xA7\x80go` a\x01`\x83\x01Q\x01R\x7F\x0E\x8D\xB2\xB2\x89=\xF2=\xD6\x81y\x96\xF7_\x10\0\x9D\x99\"\x07\x93\xECsa\x01\xC0\x82\x01QR\x7F\x19\xEB\x12\xA7\x82|\r\xDFc\x83\xFE\x80l9S\xBD\x06\xB0\x8A\xAE{\xF2\xA0\x1FU\xC9\x86\xA8OP\xCC(` a\x01\xC0\x83\x01Q\x01R\x7F\x01V\x910\x88F\xE6\x8E\xA8V\xA2\xCB$\xC9\x90?\x0C\x86\x05\xDE\xA1\x90\x82\x91\x80\xFFk\xDD\x1Ce\x08\x03a\x01\xE0\x82\x01QR\x7F\x1F\xFDx\x9B\x15[\x8A\xCB\x13\xE0\xF6\xA4\x8BP\xF7\xAA\x80\x92T\x08\x88\xD0\t\x14\x10W\xD4V\x90\x91X$` a\x01\xE0\x83\x01Q\x01R\x7F\x05E\xACz\xA6m\xCF7\x19\x98\x848\xC8\x06\xFCbM\xE5z\xB4?\x85\x809/\x88\xC8l\x13x\xCEJa\x02\0\x82\x01QR\x7F\x16\xB7\xF2P\x84.\xCFN6\x90pj\x1E\x15-zW\xF7\x0FUo\x92\x07m\xA7\x85\xFD\xD3c\xC1\x9F\xCF` a\x02\0\x83\x01Q\x01R\x7F \xCB\x7F\xF3Z\x83\xA7\xDC1@6\xE4p\xF1L0\xFB\x0E\x98\xD3]f;$;\",\xAAo\xC7\xDBDa\x02 \x82\x01QR\x7F\x14\x9FAWDpth\xBD\xAAN\x85E \x1A\xB4\r\x191\xA7\xD3\x1F#v\x8F\xA7\xC6Ut\xEE>\xAB` a\x02 \x83\x01Q\x01R\x7F\n%\xC1\xB7W9\x06\xDCN\x19;N\xA8/\xD1\xFE|\xCE\xBCM\x92]\xAD&\xF0\xFF\t\xC8L\x9F\x1Aua\x02@\x82\x01QR\x7F\nR\x1F\xF3\x0C\x8F6fy\x8F\x84|]L7\x96X\xFB\xA1\x01V\xE7\xA9I\x9F'\x13\xFA\xE9\xBF+\xE1` a\x02@\x83\x01Q\x01R\x7F\x03\xDBe\x10\xC3\xF16)\xFD\xED\x9AZ-AeK\xBC\xE4\xEFm\x02L\xADS\x10\0Q\xD4\xA3\xF3\xEB\xC9a\x02`\x82\x01QR\x7F\x08\xE8\n\\\x8EL\x9B\x9F&\xF3\0<\xC5\x94\x03\xA1\x8D16\xAF\xD00\x86\x8D%\xCC\x8B\x80~*\xB3p` a\x02`\x83\x01Q\x01R\x90V[`\0a \xBE\x82a\"^V[a \xE1\x83`\0\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[` \x02` \x01\x01Qa\x10\xEFV[a \xF7\x83`\x01\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!\r\x83`\x02\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!#\x83`\x03\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!9\x83`\x04\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!O\x83`\x05\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!e\x83`\x06\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!{\x83`\x07\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[`\0a!\x88\x85\x85\x85a#\x96V[\x90Pa!\x93\x81a%\x17V[\x91PP[\x93\x92PPPV[a!\xA7\x82a)\xDFV[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a!\xECWa\x16\xD9\x82\x82a*\x87V[a\x0C\xBCa*\xFFV[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0T`\x01`@\x1B\x90\x04`\xFF\x16a\r\x9FW`@Q\x7F\xD7\xE6\xBC\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x10,a!\xF4V[\x80Qa\"i\x90a+7V[a\"v\x81` \x01Qa+7V[a\"\x83\x81`@\x01Qa+7V[a\"\x90\x81``\x01Qa+7V[a\"\x9D\x81`\x80\x01Qa+7V[a\"\xAA\x81`\xA0\x01Qa+7V[a\"\xB7\x81`\xC0\x01Qa+7V[a\"\xC4\x81`\xE0\x01Qa+7V[a\"\xD2\x81a\x01\0\x01Qa+7V[a\"\xE0\x81a\x01 \x01Qa+7V[a\"\xEE\x81a\x01@\x01Qa+7V[a\"\xFC\x81a\x01`\x01Qa+7V[a#\n\x81a\x01\x80\x01Qa+7V[a#\x18\x81a\x01\xA0\x01Qa\x10\xEFV[a#&\x81a\x01\xC0\x01Qa\x10\xEFV[a#4\x81a\x01\xE0\x01Qa\x10\xEFV[a#B\x81a\x02\0\x01Qa\x10\xEFV[a#P\x81a\x02 \x01Qa\x10\xEFV[a#^\x81a\x02@\x01Qa\x10\xEFV[a#l\x81a\x02`\x01Qa\x10\xEFV[a#z\x81a\x02\x80\x01Qa\x10\xEFV[a#\x88\x81a\x02\xA0\x01Qa\x10\xEFV[a\x10x\x81a\x02\xC0\x01Qa\x10\xEFV[a#\x9EaT\xDDV[\x83` \x01Q\x83Q\x14a#\xDCW`@Q\x7FA\xF5;\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a#\xE9\x85\x85\x85a+\xE1V[\x90P`\0a#\xFA\x86`\0\x01Qa/\x12V[\x90P`\0a$\r\x82\x84`\xA0\x01Q\x88a2\xF6V[`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x03\xC0\x806\x837PP`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a$PW\x90PP\x90P`\0a$\x89\x8A\x85\x8A\x89\x87\x87a3VV[`\xA0\x87\x01Q``\x87\x01Q\x91\x92P\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01`\0\x81\x83\x85\t`@\x80Qa\x01\0\x81\x01\x82R`\xE0\x9C\x8D\x01Q\x81R` \x81\x01\x96\x90\x96R\x85\x01RPPP``\x81\x01\x91\x90\x91R`\x80\x81\x01\x92\x90\x92R`\xA0\x82\x01Ra\x01`\x86\x01Q`\xC0\x82\x01Ra\x01\x80\x90\x95\x01Q\x92\x85\x01\x92\x90\x92RP\x91\x94\x93PPPPV[`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x80\x83\x01\x82\x90R\x83Q\x80\x85\x01\x85R\x82\x81R\x90\x81\x01\x82\x90R\x83Q`\x02\x80\x82R``\x82\x01\x90\x95R\x91\x93\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x93\x92\x85\x91\x81` \x01` \x82\x02\x806\x837PP`@\x80Q`\x02\x80\x82R``\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a%\x9DW\x90PP\x90P`\0`\x01\x90P\x80\x83`\0\x81Q\x81\x10a%\xE0Wa%\xE0aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x87`\xC0\x01Q\x82`\0\x81Q\x81\x10a&\x04Wa&\x04aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x87`\0\x01Q\x83`\x01\x81Q\x81\x10a&'Wa&'aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x87`\xE0\x01Q\x82`\x01\x81Q\x81\x10a&KWa&KaZ\xF2V[` \x02` \x01\x01\x81\x90RPa&`\x82\x84a3\x8BV[`\x80\x89\x01QQ\x90\x95P``\x93P\x83\x92P\x90P`\0a&\x7F\x82`\x02a[!V[a&\x8A\x90`\x01a[!V[\x90P\x80g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a&\xA5Wa&\xA5aU\x9EV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a&\xCEW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x80g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a&\xEAWa&\xEAaU\x9EV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a'/W\x81` \x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a'\x08W\x90P[P\x92PPP`\0\x80`\0[\x89`\x80\x01QQ\x81\x10\x15a'\xD3W\x89`\x80\x01Q\x81\x81Q\x81\x10a']Wa']aZ\xF2V[` \x02` \x01\x01Q\x85\x83\x81Q\x81\x10a'wWa'waZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x89`\xA0\x01Q\x81\x81Q\x81\x10a'\x99Wa'\x99aZ\xF2V[` \x02` \x01\x01Q\x84\x83\x81Q\x81\x10a'\xB3Wa'\xB3aZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra'\xC9`\x01\x83a[!V[\x91P`\x01\x01a':V[P\x88` \x01Q\x84\x82\x81Q\x81\x10a'\xEBWa'\xEBaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88`\xC0\x01Q\x83\x82\x81Q\x81\x10a(\x0EWa(\x0EaZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra($`\x01\x82a[!V[\x89Q`@\x8B\x01Q\x91\x92P\x90`\0\x89\x82\x84\t\x90P\x80\x87\x85\x81Q\x81\x10a(JWa(JaZ\xF2V[` \x02` \x01\x01\x81\x81RPPPPP\x88`\xE0\x01Q\x83\x82\x81Q\x81\x10a(pWa(paZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra(\x86`\x01\x82a[!V[``\x8A\x01Q\x90\x91P\x87\x81\x84\x08\x92PPa(\x9E\x82a4\x85V[\x84\x82\x81Q\x81\x10a(\xB0Wa(\xB0aZ\xF2V[` \x02` \x01\x01\x81\x81RPPa(\xE8`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x91\x82\x01R\x81Q\x80\x83\x01\x90\x92R`\x01\x82R`\x02\x90\x82\x01R\x90V[\x83\x82\x81Q\x81\x10a(\xFAWa(\xFAaZ\xF2V[` \x02` \x01\x01\x81\x90RPa)\x17a)\x12\x84\x86a3\x8BV[a4\xDBV[\x94PPPPP`\0`@Q\x80`\x80\x01`@R\x80\x7F\x01\x18\xC4\xD5\xB87\xBC\xC2\xBC\x89\xB5\xB3\x98\xB5\x97N\x9FYD\x07;2\x07\x8B~#\x1F\xEC\x93\x88\x83\xB0\x81R` \x01\x7F&\x0E\x01\xB2Q\xF6\xF1\xC7\xE7\xFFNX\x07\x91\xDE\xE8\xEAQ\xD8z5\x8E\x03\x8BN\xFE0\xFA\xC0\x93\x83\xC1\x81R` \x01\x7F\"\xFE\xBD\xA3\xC0\xC0c*VG[B\x14\xE5a^\x11\xE6\xDD?\x96\xE6\xCE\xA2\x85J\x87\xD4\xDA\xCC^U\x81R` \x01\x7F\x04\xFCci\xF7\x11\x0F\xE3\xD2QV\xC1\xBB\x9Ar\x85\x9C\xF2\xA0FA\xF9\x9B\xA4\xEEA<\x80\xDAj_\xE4\x81RP\x90Pa)\xD5\x83\x82\x84a)\xD0a5zV[a6KV[\x96\x95PPPPPPV[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a*.W`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16`\x04\x82\x01R`$\x01a\nvV[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa*\xA4\x91\x90a[4V[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a*\xDFW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a*\xE4V[``\x91P[P\x91P\x91Pa*\xF4\x85\x83\x83a7/V[\x92PPP[\x92\x91PPV[4\x15a\r\x9FW`@Q\x7F\xB3\x98\x97\x9F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80Q` \x82\x01Q`\0\x91\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\x91\x15\x90\x15\x16\x15a+qWPPPV[\x82Q` \x84\x01Q\x82`\x03\x84\x85\x85\x86\t\x85\t\x08\x83\x82\x83\t\x14\x83\x82\x10\x84\x84\x10\x16\x16\x93PPP\x81a\x16\xD9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x17`$\x82\x01R\x7FBn254: invalid G1 point\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[a,)`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@\x80Q\x80\x82\x01\x90\x91R``\x81R`\0` \x82\x01R\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01a,j\x82\x87\x87a7\xA4V[\x81Q\x84Qa,w\x90a;\x85V[a,\x84\x86` \x01Qa;\x85V[a,\x91\x87`@\x01Qa;\x85V[a,\x9E\x88``\x01Qa;\x85V[a,\xAB\x89`\x80\x01Qa;\x85V[`@Q` \x01a,\xC0\x96\x95\x94\x93\x92\x91\x90a[PV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\xDB\x82a\xF2I\x065\xEB\xA5\x0C\xB9\xC2\xE5\xE7P\x80\0\x01\x90\x82\x01R\x7F\t\xC52\xC60k\x93\xD2\x96x \rG\xC0\xB2\xA9\x9C\x18\xD5\x1B\x83\x8E\xEB\x1D>\xEDLS;\xB5\x12\xD0``\x82\x01R\x7F'$q6\x03\xBF\xBDy\n\xEA\xF3\xE7\xDF%\xD8\xE7\xEF\x8F1\x134\x90[M\x8C\x99\x98\x0C\xF2\x10\x97\x9D`\x80\x82\x01R\x90V[`@Q\x7F\xE2\xEF\t\xE5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x91\x90PV[a3\x1A`@Q\x80``\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[a3$\x84\x84a>\x08V[\x80\x82Ra34\x90\x85\x90\x85\x90a>nV[` \x82\x01R\x80Qa3J\x90\x85\x90\x84\x90\x86\x90a>\xF4V[`@\x82\x01R\x93\x92PPPV[`\0\x80a3d\x85\x87\x89a@\xBBV[\x90Pa3t\x88\x86\x89\x89\x88\x88aA\xB9V[a3\x7F\x81\x87\x86aD\xD7V[\x98\x97PPPPPPPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x82Q\x82Q\x14a3\xF0W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FMSM error: length does not match`D\x82\x01R`d\x01a\nvV[a4.\x83`\0\x81Q\x81\x10a4\x06Wa4\x06aZ\xF2V[` \x02` \x01\x01Q\x83`\0\x81Q\x81\x10a4!Wa4!aZ\xF2V[` \x02` \x01\x01QaE9V[\x90P`\x01[\x82Q\x81\x10\x15a4~Wa4t\x82a4o\x86\x84\x81Q\x81\x10a4UWa4UaZ\xF2V[` \x02` \x01\x01Q\x86\x85\x81Q\x81\x10a4!Wa4!aZ\xF2V[aE\xDDV[\x91P`\x01\x01a43V[P\x92\x91PPV[`\0a4\xB1\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x83a\\\x93V[a*\xF9\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01a\\\xB5V[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81Q` \x83\x01Q\x15\x90\x15\x16\x15a5\x03WP\x90V[`@Q\x80`@\x01`@R\x80\x83`\0\x01Q\x81R` \x01\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\x84` \x01Qa5H\x91\x90a\\\x93V[a5r\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDGa\\\xB5V[\x90R\x92\x91PPV[a5\xA5`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80`\x80\x01`@R\x80\x7F\x18\0\xDE\xEF\x12\x1F\x1EvBj\0f^\\DygC\"\xD4\xF7^\xDA\xDDF\xDE\xBD\\\xD9\x92\xF6\xED\x81R` \x01\x7F\x19\x8E\x93\x93\x92\rH:r`\xBF\xB71\xFB]%\xF1\xAAI35\xA9\xE7\x12\x97\xE4\x85\xB7\xAE\xF3\x12\xC2\x81R` \x01\x7F\x12\xC8^\xA5\xDB\x8Cm\xEBJ\xABq\x80\x8D\xCB@\x8F\xE3\xD1\xE7i\x0CC\xD3{L\xE6\xCC\x01f\xFA}\xAA\x81R` \x01\x7F\t\x06\x89\xD0X_\xF0u\xEC\x9E\x99\xADi\x0C3\x95\xBCK13p\xB3\x8E\xF3U\xAC\xDA\xDC\xD1\"\x97[\x81RP\x90P\x90V[`\0\x80`\0`@Q\x87Q\x81R` \x88\x01Q` \x82\x01R` \x87\x01Q`@\x82\x01R\x86Q``\x82\x01R``\x87\x01Q`\x80\x82\x01R`@\x87\x01Q`\xA0\x82\x01R\x85Q`\xC0\x82\x01R` \x86\x01Q`\xE0\x82\x01R` \x85\x01Qa\x01\0\x82\x01R\x84Qa\x01 \x82\x01R``\x85\x01Qa\x01@\x82\x01R`@\x85\x01Qa\x01`\x82\x01R` `\0a\x01\x80\x83`\x08Z\xFA\x91PP`\0Q\x91P\x80a7!W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1C`$\x82\x01R\x7FBn254: Pairing check failed!\0\0\0\0`D\x82\x01R`d\x01a\nvV[P\x15\x15\x90P[\x94\x93PPPPV[``\x82a7DWa7?\x82aF\x84V[a!\x97V[\x81Q\x15\x80\x15a7[WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a7\x9DW`@Q\x7F\x99\x96\xB3\x15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x85\x16`\x04\x82\x01R`$\x01a\nvV[P\x80a!\x97V[\x82Q`\xFE\x90a7\xDFa7\xB5\x83a<\xBCV[`@Q` \x01a7\xC7\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x04aF\xC6V[a8\x19a7\xEF\x86`\0\x01Qa<\xBCV[`@Q` \x01a8\x01\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x08aF\xC6V[a8)a7\xEF\x87` \x01Qa<\xBCV[`@Q` \x01a8<\x94\x93\x92\x91\x90a\\\xC8V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85Ra8Y`\x01a<\xBCV[a8\x82\x7F/\x8D\xD1\xF1\xA7X^W\x84\x93P`\0[\x82\x81\x10\x15a>RW\x83\x85\x86\t\x94P`\x01\x01a>eV[`\x01\x83\x03\x93P[PPP\x92\x91PPV[`\0\x82`\x01\x03a>\x80WP`\x01a!\x97V[\x81`\0\x03a>\x90WP`\0a!\x97V[`@\x84\x01Q\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90`\0\x90\x82\x81\x86\t\x90P\x85\x80\x15a>\xD2W`\x01\x87\x03\x92Pa>\xD9V[`\x01\x84\x03\x92P[Pa>\xE3\x82aG\xEEV[\x91P\x82\x82\x82\t\x97\x96PPPPPPPV[\x82Q`\0\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90\x83\x83\x03a?\x87W`\x01`\0[\x82\x81\x10\x15a?zW\x81\x87\x03a?[W\x87\x81\x81Q\x81\x10a?HWa?HaZ\xF2V[` \x02` \x01\x01Q\x94PPPPPa7'V[\x83\x80a?iWa?ia\\}V[\x89``\x01Q\x83\t\x91P`\x01\x01a?'V[P`\0\x93PPPPa7'V[`\0\x80`\0\x80\x8A`@\x01Q\x90P`\0\x80a?\xA1\x8D\x88aH\xA6V[\x90P`\0\x87g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a?\xBEWa?\xBEaU\x9EV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a?\xE7W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P\x88\x8B\x85\t\x93P`\x01\x92P`\0[\x88\x81\x10\x15a@,W` \x81\x02` \x84\x01\x01Q\x95P\x89\x8D\x87\x8C\x03\x08\x96P\x89\x87\x85\t` \x82\x81\x02\x84\x01\x01\x88\x90R\x93P`\x01\x01a?\xF7V[Pa@6\x83aG\xEEV[\x92P`\0[\x88\x81\x10\x15a@\xA9W` \x81\x02` \x84\x01\x01Q\x95P\x89\x86\x86\t\x97P\x89\x84\x89\t\x97P`\0[\x89\x81\x10\x15a@\x88W\x80\x82\x14a@\x80W` \x81\x02` \x84\x01\x01Q\x97P\x8A\x88\x8A\t\x98P[`\x01\x01a@^V[P` \x81\x02` \x8F\x01\x01Q\x95P\x89\x86\x89\t\x97P\x89\x88\x8C\x08\x9AP`\x01\x01a@;V[PPPPPPPPPP\x94\x93PPPPV[`\0\x80\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P`\0\x83` \x01Q\x90P`\0\x84`@\x01Q\x90P`\0`\x01\x90P``\x88\x01Q`\x80\x89\x01Qa\x01\xA0\x89\x01Qa\x02@\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xC0\x89\x01Qa\x02`\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xE0\x89\x01Qa\x02\x80\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02\0\x89\x01Qa\x02\xA0\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02 \x89\x01Q\x91Pa\x02\xC0\x89\x01Q\x86\x87\x82\x89\x85\x87\x08\t\x85\t\x93PPPP\x87Q` \x89\x01Q\x85\x86\x86\x83\t\x87\x03\x85\x08\x96PP\x84\x85\x83\x83\t\x86\x03\x87\x08\x99\x98PPPPPPPPPV[aA\xC7\x86\x86\x86\x86\x85\x87aI\x97V[`\xC0\x85\x01Q\x82Q\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x91\x90\x81\x90\x81\x90\x86\x90`\x14\x90\x81\x10aB\x08WaB\x08aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85`\0\x01Q\x84`\x14\x81Q\x81\x10aB,WaB,aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x15\x81Q\x81\x10aBQWaBQaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85` \x01Q\x84`\x15\x81Q\x81\x10aBuWaBuaZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x16\x81Q\x81\x10aB\x9AWaB\x9AaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85`@\x01Q\x84`\x16\x81Q\x81\x10aB\xBEWaB\xBEaZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x17\x81Q\x81\x10aB\xE3WaB\xE3aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85``\x01Q\x84`\x17\x81Q\x81\x10aC\x07WaC\x07aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x18\x81Q\x81\x10aC,WaC,aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85`\x80\x01Q\x84`\x18\x81Q\x81\x10aCPWaCPaZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x19\x81Q\x81\x10aCuWaCuaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88`@\x01Q\x84`\x19\x81Q\x81\x10aC\x99WaC\x99aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1A\x81Q\x81\x10aC\xBEWaC\xBEaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88``\x01Q\x84`\x1A\x81Q\x81\x10aC\xE2WaC\xE2aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1B\x81Q\x81\x10aD\x07WaD\x07aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88`\x80\x01Q\x84`\x1B\x81Q\x81\x10aD+WaD+aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1C\x81Q\x81\x10aDPWaDPaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88`\xA0\x01Q\x84`\x1C\x81Q\x81\x10aDtWaDtaZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x87`\xE0\x01Q\x85`\x1D\x81Q\x81\x10aD\x9DWaD\x9DaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85`\xA0\x01Q\x84`\x1D\x81Q\x81\x10aD\xC1WaD\xC1aZ\xF2V[` \x02` \x01\x01\x81\x90RPPPPPPPPPPV[\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x83\x81\x03\x90`\0[`\n\x81\x10\x15aE0W` `\x15\x82\x01\x02\x84\x01Q` \x82\x02a\x01\xA0\x01\x86\x01Q\x83\x84\x82\x84\t\x86\x08\x94PPP`\x01\x01aD\xFFV[PP\x93\x92PPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RaEUaU0V[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x81\x01\x83\x90R`\0``\x83`\x80\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80aE\x87W`\0\x80\xFD[P\x80aE\xD5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01R\x7FBn254: scalar mul failed!\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[PP\x92\x91PPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RaE\xF9aUNV[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x83\x01R\x83\x01Q``\x80\x83\x01\x91\x90\x91R`\0\x90\x83`\xC0\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80aF6W`\0\x80\xFD[P\x80aE\xD5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: group addition failed!\0\0\0`D\x82\x01R`d\x01a\nvV[\x80Q\x15aF\x94W\x80Q\x80\x82` \x01\xFD[`@Q\x7F\x14%\xEAB\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x81aF\xD4\x81`\x1Fa[!V[\x10\x15aG\"W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x0E`$\x82\x01R\x7Fslice_overflow\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[aG,\x82\x84a[!V[\x84Q\x10\x15aG|W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x11`$\x82\x01R\x7Fslice_outOfBounds\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[``\x82\x15\x80\x15aG\x9BW`@Q\x91P`\0\x82R` \x82\x01`@RaG\xE5V[`@Q\x91P`\x1F\x84\x16\x80\x15` \x02\x81\x84\x01\x01\x85\x81\x01\x87\x83\x15` \x02\x84\x8B\x01\x01\x01[\x81\x83\x10\x15aG\xD4W\x80Q\x83R` \x92\x83\x01\x92\x01aG\xBCV[PP\x85\x84R`\x1F\x01`\x1F\x19\x16`@RP[P\x94\x93PPPPV[`\0\x80`\0\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P`@Q` \x81R` \x80\x82\x01R` `@\x82\x01R\x84``\x82\x01R`\x02\x82\x03`\x80\x82\x01R\x81`\xA0\x82\x01R` `\0`\xC0\x83`\x05Z\xFA\x92PP`\0Q\x92P\x81aH\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: pow precompile failed!\0\0\0`D\x82\x01R`d\x01a\nvV[PP\x91\x90PV[``\x82` \x01Q\x82\x11\x15aH\xE6W`@Q\x7F\x8C^\x11\xF1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x83\x01Q`\x01\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x84g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15aI'WaI'aU\x9EV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15aIPW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x84\x15\x19\x15a>eW` \x84\x01\x85` \x02\x81\x01`\x01\x82R` \x82\x01\x91P[\x80\x82\x10\x15aI\x8CW\x82\x85\x85\t\x93P\x83\x82R` \x82\x01\x91PaIpV[PPPPP\x92\x91PPV[`\0\x80`\0\x80`\0\x80\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P\x80` \x8B\x01Q` \x8D\x01Q\t\x95P\x8AQ\x93P\x80`\xA0\x8C\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80\x7F/\x8D\xD1\xF1\xA7X#\xA9f.\xFC\x9C\"\x9Cj\0\x80Th\x01\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15b\0\0\xA7W`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80T`\x01`\x01`@\x1B\x03\x90\x81\x16\x14b\0\x01\x07W\x80T`\x01`\x01`@\x1B\x03\x19\x16`\x01`\x01`@\x1B\x03\x90\x81\x17\x82U`@Q\x90\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PV[\x81Q`\x01`\x01`@\x1B\x03\x16\x15\x15\x80b\0\x01/WP` \x82\x01Q`\x01`\x01`@\x1B\x03\x16\x15\x15[\x80b\0\x01=WP`\x80\x82\x01Q\x15[\x80b\0\x01KWP`\xA0\x82\x01Q\x15[\x80b\0\x01YWP`\xC0\x82\x01Q\x15[\x80b\0\x01gWP`\xE0\x82\x01Q\x15[\x80b\0\x01wWPc\xFF\xFF\xFF\xFF\x81\x16\x15[\x15b\0\x01\x96W`@QcP\xDD\x03\xF7`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x81`\x05`\0\x80`\x04\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP\x81`\x05`\0\x80`\x08\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP`\0\x80`\x0Ca\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP\x80`\0\x80a\x01\0\n\x81T\x81c\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83c\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`\0b\0\x03\x84\x83b\0\x04r` \x1B` \x1CV[`\x01\x81\x81U`\xE0\x85\x01Q`\x02\x81\x81U`\x03\x93\x90\x93U`\x04U`\x07\x80T\x80\x83\x01\x82U`\0\x91\x82RC\x7F\xA6l\xC9(\xB5\xED\xB8*\xF9\xBDI\x92)T\x15Z\xB7\xB0\x94&\x94\xBE\xA4\xCEDf\x1D\x9A\x876\xC6\x88\x90\x91\x01U`@\x80Q\x80\x82\x01\x82R` \x80\x89\x01Q`\x01`\x01`@\x1B\x03\x90\x81\x16\x83R\x92\x90\x98\x01Q\x97\x81\x01\x97\x88R`\x08\x80T\x94\x85\x01\x81U\x90\x92R\x90Q\x7F\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE3\x92\x90\x93\x02\x91\x82\x01\x80T`\x01`\x01`@\x1B\x03\x19\x16\x93\x90\x91\x16\x92\x90\x92\x17\x90\x91U\x92Q\x7F\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE4\x90\x93\x01\x92\x90\x92UPPV[`\x80\x80\x82\x01Q`\xA0\x83\x01Q`\xC0\x84\x01Q`@\x80Q` \x81\x01\x94\x90\x94R\x83\x01\x91\x90\x91R``\x82\x01R`\0\x91\x01`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[\x80Q`\x01`\x01`@\x1B\x03\x81\x16\x81\x14b\0\x04\xD2W`\0\x80\xFD[\x91\x90PV[\x80Qc\xFF\xFF\xFF\xFF\x81\x16\x81\x14b\0\x04\xD2W`\0\x80\xFD[`\0\x80\x82\x84\x03a\x01 \x81\x12\x15b\0\x05\x02W`\0\x80\xFD[a\x01\0\x80\x82\x12\x15b\0\x05\x13W`\0\x80\xFD[`@Q\x91P\x80\x82\x01`\x01`\x01`@\x1B\x03\x81\x11\x83\x82\x10\x17\x15b\0\x05EWcNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Rb\0\x05S\x85b\0\x04\xBAV[\x82Rb\0\x05c` \x86\x01b\0\x04\xBAV[` \x83\x01R`@\x85\x01Q`@\x83\x01R``\x85\x01Q``\x83\x01R`\x80\x85\x01Q`\x80\x83\x01R`\xA0\x85\x01Q`\xA0\x83\x01R`\xC0\x85\x01Q`\xC0\x83\x01R`\xE0\x85\x01Q`\xE0\x83\x01R\x81\x93Pb\0\x05\xB4\x81\x86\x01b\0\x04\xD7V[\x92PPP\x92P\x92\x90PV[`\x80Qa^\x88b\0\x05\xE9`\09`\0\x81\x81a\x16)\x01R\x81\x81a\x16R\x01Ra\x17\xBE\x01Ra^\x88`\0\xF3\xFE`\x80`@R`\x046\x10a\x01~W`\x005`\xE0\x1C\x80c\x01?\xA5\xFC\x14a\x01\x83W\x80c\r\x8En,\x14a\x01\xA5W\x80c *\n\xDB\x14a\x01\xD7W\x80c1=\xF7\xB1\x14a\x02\x80W\x80c8+!Z\x14a\x02\xADW\x80c9\x194\x0F\x14a\x02\xD1W\x80c9I\xD1\xE9\x14a\x02\xF1W\x80c@\x999\xB7\x14a\x038W\x80cHG\xAE]\x14a\x03XW\x80cO\x1E\xF2\x86\x14a\x03\xDAW\x80cR\xD1\x90-\x14a\x03\xEDW\x80cS\x0C\xA7\x8F\x14a\x04\x02W\x80cTd`\x85\x14a\x04\"W\x80cb\x82w3\x14a\x047W\x80ci\xCCj\x04\x14a\x04MW\x80cpS\xFCQ\x14a\x04bW\x80cqP\x18\xA6\x14a\x04wW\x80cvg\x18\x08\x14a\x04\x8CW\x80cv\xB6\xB7\xCB\x14a\x04\xC0W\x80c\x7F\x17\xBA\xAD\x14a\x04\xD6W\x80c\x82\xD0\x7F\xF3\x14a\x05\x89W\x80c\x85\x84\xD2?\x14a\x05\x9EW\x80c\x8D\xA5\xCB[\x14a\x05\xE2W\x80c\xA2D\xD5\x96\x14a\x05\xF7W\x80c\xA5\x1Eo\xEA\x14a\x06\x17W\x80c\xAA\x92'2\x14a\x067W\x80c\xAD<\xB1\xCC\x14a\x06WW\x80c\xBD2Q\x9A\x14a\x06\x95W\x80c\xCAo\xE8U\x14a\x06\xC6W\x80c\xDB\x13\xB6\n\x14a\x06\xDCW\x80c\xE003\x01\x14a\x07\x1BW\x80c\xF0h T\x14a\x07;W\x80c\xF2\xFD\xE3\x8B\x14a\x07mW[`\0\x80\xFD[4\x80\x15a\x01\x8FW`\0\x80\xFD[Pa\x01\xA3a\x01\x9E6`\x04aRXV[a\x07\x8DV[\0[4\x80\x15a\x01\xB1W`\0\x80\xFD[P`@\x80Q`\x01\x81R`\0` \x82\x01\x81\x90R\x91\x81\x01\x91\x90\x91R``\x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\xE3W`\0\x80\xFD[Pa\x01\xA3a\x01\xF26`\x04aS\xB8V[`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x85Q\x81T\x92\x87\x01Q`\x01`\x01`@\x1B\x03\x90\x81\x16\x90\x95\x02`\x01`\x01`\x80\x1B\x03\x19\x90\x93\x16\x94\x16\x93\x90\x93\x17\x17\x82U\x91\x83\x01Q`\x01\x82\x01U``\x83\x01Q`\x02\x82\x01U`\x80\x83\x01Q`\x03\x82\x01U`\xA0\x83\x01Q`\x04\x82\x01U`\xC0\x83\x01Q\x91\x81\x01\x91\x90\x91U`\xE0\x90\x91\x01Q`\x06\x90\x91\x01UV[4\x80\x15a\x02\x8CW`\0\x80\xFD[P`\x06Ta\x02\xA0\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Qa\x01\xCE\x91\x90aS\xD5V[4\x80\x15a\x02\xB9W`\0\x80\xFD[Pa\x02\xC3`\x03T\x81V[`@Q\x90\x81R` \x01a\x01\xCEV[4\x80\x15a\x02\xDDW`\0\x80\xFD[Pa\x01\xA3a\x02\xEC6`\x04aT\x0CV[a\x08MV[4\x80\x15a\x02\xFDW`\0\x80\xFD[Pa\x01\xA3a\x03\x0C6`\x04aT\xA1V[`\0\x80T`\x01`\x01`@\x1B\x03\x90\x92\x16`\x01``\x1B\x02`\x01``\x1B`\x01`\xA0\x1B\x03\x19\x90\x92\x16\x91\x90\x91\x17\x90UV[4\x80\x15a\x03DW`\0\x80\xFD[Pa\x01\xA3a\x03S6`\x04aT\xECV[a\x08pV[4\x80\x15a\x03dW`\0\x80\xFD[Pa\x03ma\x0B\x9BV[`@Qa\x01\xCE\x91\x90`\0a\x01\0\x82\x01\x90P`\x01\x80`@\x1B\x03\x80\x84Q\x16\x83R\x80` \x85\x01Q\x16` \x84\x01RP`@\x83\x01Q`@\x83\x01R``\x83\x01Q``\x83\x01R`\x80\x83\x01Q`\x80\x83\x01R`\xA0\x83\x01Q`\xA0\x83\x01R`\xC0\x83\x01Q`\xC0\x83\x01R`\xE0\x83\x01Q`\xE0\x83\x01R\x92\x91PPV[a\x01\xA3a\x03\xE86`\x04aV\xA6V[a\x0C-V[4\x80\x15a\x03\xF9W`\0\x80\xFD[Pa\x02\xC3a\x0CHV[4\x80\x15a\x04\x0EW`\0\x80\xFD[Pa\x01\xA3a\x04\x1D6`\x04aWKV[a\x0CeV[4\x80\x15a\x04.W`\0\x80\xFD[P`\x08Ta\x02\xC3V[4\x80\x15a\x04CW`\0\x80\xFD[Pa\x02\xC3`\x02T\x81V[4\x80\x15a\x04YW`\0\x80\xFD[Pa\x01\xA3a\x0C\xE1V[4\x80\x15a\x04nW`\0\x80\xFD[P`\x07Ta\x02\xC3V[4\x80\x15a\x04\x83W`\0\x80\xFD[Pa\x01\xA3a\rQV[4\x80\x15a\x04\x98W`\0\x80\xFD[P`\0Ta\x04\xB3\x90`\x01``\x1B\x90\x04`\x01`\x01`@\x1B\x03\x16\x81V[`@Qa\x01\xCE\x91\x90aW\xFCV[4\x80\x15a\x04\xCCW`\0\x80\xFD[Pa\x02\xC3`\x01T\x81V[4\x80\x15a\x04\xE2W`\0\x80\xFD[Pa\x05Ca\x04\xF16`\x04aX$V[`\x05` \x81\x90R`\0\x91\x82R`@\x90\x91 \x80T`\x01\x82\x01T`\x02\x83\x01T`\x03\x84\x01T`\x04\x85\x01T\x95\x85\x01T`\x06\x90\x95\x01T`\x01`\x01`@\x1B\x03\x80\x86\x16\x97`\x01`@\x1B\x90\x96\x04\x16\x95\x93\x94\x92\x93\x91\x92\x91\x90\x88V[`@\x80Q`\x01`\x01`@\x1B\x03\x99\x8A\x16\x81R\x98\x90\x97\x16` \x89\x01R\x95\x87\x01\x94\x90\x94R``\x86\x01\x92\x90\x92R`\x80\x85\x01R`\xA0\x84\x01R`\xC0\x83\x01R`\xE0\x82\x01Ra\x01\0\x01a\x01\xCEV[4\x80\x15a\x05\x95W`\0\x80\xFD[Pa\x03ma\rcV[4\x80\x15a\x05\xAAW`\0\x80\xFD[Pa\x05\xBEa\x05\xB96`\x04aX?V[a\r\xF3V[`@\x80Q\x82Q`\x01`\x01`@\x1B\x03\x16\x81R` \x92\x83\x01Q\x92\x81\x01\x92\x90\x92R\x01a\x01\xCEV[4\x80\x15a\x05\xEEW`\0\x80\xFD[Pa\x02\xA0a\x0FMV[4\x80\x15a\x06\x03W`\0\x80\xFD[Pa\x01\xA3a\x06\x126`\x04aXXV[a\x0FhV[4\x80\x15a\x06#W`\0\x80\xFD[Pa\x02\xC3a\x0626`\x04aX?V[a\x10\x93V[4\x80\x15a\x06CW`\0\x80\xFD[Pa\x02\xC3a\x06R6`\x04aS\xB8V[a\x10\xB4V[4\x80\x15a\x06cW`\0\x80\xFD[Pa\x06\x88`@Q\x80`@\x01`@R\x80`\x05\x81R` \x01d\x03R\xE3\x02\xE3`\xDC\x1B\x81RP\x81V[`@Qa\x01\xCE\x91\x90aX\xC3V[4\x80\x15a\x06\xA1W`\0\x80\xFD[P`\x06Ta\x06\xB6\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Q\x90\x15\x15\x81R` \x01a\x01\xCEV[4\x80\x15a\x06\xD2W`\0\x80\xFD[Pa\x02\xC3`\x04T\x81V[4\x80\x15a\x06\xE8W`\0\x80\xFD[Pa\x06\xFCa\x06\xF76`\x04aX?V[a\x10\xFCV[`@\x80Q`\x01`\x01`@\x1B\x03\x90\x93\x16\x83R` \x83\x01\x91\x90\x91R\x01a\x01\xCEV[4\x80\x15a\x07'W`\0\x80\xFD[Pa\x06\xB6a\x0766`\x04aX\xF6V[a\x114V[4\x80\x15a\x07GW`\0\x80\xFD[P`\0Ta\x07X\x90c\xFF\xFF\xFF\xFF\x16\x81V[`@Qc\xFF\xFF\xFF\xFF\x90\x91\x16\x81R` \x01a\x01\xCEV[4\x80\x15a\x07yW`\0\x80\xFD[Pa\x01\xA3a\x07\x886`\x04aRXV[a\x12\x17V[a\x07\x95a\x12UV[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x07\xBCW`@Qc\xE6\xC4${`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x06T`\x01`\x01`\xA0\x1B\x03\x90\x81\x16\x90\x82\x16\x03a\x07\xEBW`@Qc\xA8c\xAE\xC9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x06\x80T`\x01`\x01`\xA8\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x80\x84\x16\x91\x90\x91\x17`\x01`\xA0\x1B\x17\x91\x82\x90U`@Q\x7F\x80\x17\xBB\x88\x7F\xDF\x8F\xCAC\x14\xA9\xD4\x0Fns\xB3\xB8\x10\x02\xD6~\\\xFA\x85\xD8\x81s\xAFj\xA4`r\x92a\x08B\x92\x16\x90aS\xD5V[`@Q\x80\x91\x03\x90\xA1PV[a\x08Y`\x07`\0aO\x91V[\x80Qa\x08l\x90`\x07\x90` \x84\x01\x90aO\xAFV[PPV[`\x06T`\x01`\xA0\x1B\x90\x04`\xFF\x16\x80\x15a\x08\x94WP`\x06T`\x01`\x01`\xA0\x1B\x03\x163\x14\x15[\x15a\x08\xDBW`\x06T`\x01`\x01`\xA0\x1B\x03\x16a\x08\xC2W`@Qc\x12\xE6\xD1\xE7`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`@Qc\x01GL\x8F`\xE7\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x08\xE3a\rcV[Q\x82Q`\x01`\x01`@\x1B\x03\x91\x82\x16\x91\x16\x11\x15\x80a\t!WPa\t\x03a\rcV[` \x01Q`\x01`\x01`@\x1B\x03\x16\x82` \x01Q`\x01`\x01`@\x1B\x03\x16\x11\x15[\x15a\t?W`@Qc\x05\x1CF\xEF`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0\x80Ta\td\x90c\xFF\xFF\xFF\xFF\x81\x16\x90`\x01``\x1B\x90\x04`\x01`\x01`@\x1B\x03\x16aY.V[`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` R`@\x90\x91 T\x91\x92P\x90\x04`\x01`\x01`@\x1B\x03\x90\x81\x16\x90\x82\x16\x14\x80\x15\x81a\t\xBAWP\x81`\x01`\x01`@\x1B\x03\x16\x84` \x01Q`\x01`\x01`@\x1B\x03\x16\x11[\x15a\t\xE3W\x81`@Qc\x03df\xBF`\xE3\x1B\x81R`\x04\x01a\t\xDA\x91\x90aW\xFCV[`@Q\x80\x91\x03\x90\xFD[a\t\xF0\x84`@\x01Qa\x12\x87V[a\t\xFD\x84``\x01Qa\x12\x87V[a\n\n\x84`\x80\x01Qa\x12\x87V[a\n\x17\x84`\xA0\x01Qa\x12\x87V[a\n$\x84`\xC0\x01Qa\x12\x87V[\x80\x15a\n2Wa\n2a\x12\xE3V[a\n<\x84\x84a\x14.V[`\0\x80T`\x01`@\x1B\x90\x81\x90\x04c\xFF\xFF\xFF\xFF\x16\x82R`\x05` \x81\x81R`@\x80\x85 \x89Q\x81T\x8B\x85\x01\x80Q`\x01`\x01`@\x1B\x03\x93\x84\x16`\x01`\x01`\x80\x1B\x03\x19\x90\x93\x16\x92\x90\x92\x17\x91\x83\x16\x90\x97\x02\x17\x82U\x8A\x83\x01\x80Q`\x01\x80\x85\x01\x91\x90\x91U``\x8D\x01Q`\x02\x80\x86\x01\x91\x90\x91U`\x80\x8E\x01Q`\x03\x86\x01U`\xA0\x8E\x01Q`\x04\x86\x01U`\xC0\x8E\x01Q\x97\x85\x01\x97\x90\x97U`\xE0\x8D\x01Q`\x06\x90\x94\x01\x93\x90\x93U`\x07\x80T\x80\x85\x01\x82U\x90\x89RC`\0\x80Q` a]\\\x839\x81Q\x91R\x90\x91\x01U\x83Q\x80\x85\x01\x85R\x87Q\x83\x16\x81R\x81Q\x81\x87\x01\x90\x81R`\x08\x80T\x95\x86\x01\x81U\x90\x99RQ`\0\x80Q` a]\xDC\x839\x81Q\x91R\x93\x90\x96\x02\x92\x83\x01\x80T`\x01`\x01`@\x1B\x03\x19\x16\x96\x83\x16\x96\x90\x96\x17\x90\x95U\x95Q`\0\x80Q` a^\x1C\x839\x81Q\x91R\x90\x91\x01U\x92Q\x88Q\x92Q\x93Q\x93\x84R\x84\x16\x93\x91\x90\x91\x16\x91\x7F\xA0Jw9$PZA\x85d67%\xF5h2\xF5w.k\x8D\r\xBDn\xFC\xE7$\xDF\xE8\x03\xDA\xE6\x91\x01`@Q\x80\x91\x03\x90\xA3PPPPV[a\x0B\xA3aO\xFAV[P`\0\x80T`\x01` \x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05` \x81\x81R`@\x92\x83\x90 \x83Qa\x01\0\x81\x01\x85R\x81T`\x01`\x01`@\x1B\x03\x80\x82\x16\x83R`\x01`@\x1B\x90\x91\x04\x16\x92\x81\x01\x92\x90\x92R`\x01\x81\x01T\x93\x82\x01\x93\x90\x93R`\x02\x83\x01T``\x82\x01R`\x03\x83\x01T`\x80\x82\x01R`\x04\x83\x01T`\xA0\x82\x01R\x90\x82\x01T`\xC0\x82\x01R`\x06\x90\x91\x01T`\xE0\x82\x01R\x90V[a\x0C5a\x16\x1EV[a\x0C>\x82a\x16\xC3V[a\x08l\x82\x82a\x16\xFAV[`\0a\x0CRa\x17\xB3V[P`\0\x80Q` a]\x9C\x839\x81Q\x91R\x90V[a\x0Cq`\x08`\0aPQV[`\0[\x81Q\x81\x10\x15a\x08lW`\x08\x82\x82\x81Q\x81\x10a\x0C\x91Wa\x0C\x91aYQV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01Q\x82T`\x01\x80\x82\x01\x85U`\0\x94\x85R\x93\x83\x90 \x82Q`\x02\x90\x92\x02\x01\x80T`\x01`\x01`@\x1B\x03\x19\x16`\x01`\x01`@\x1B\x03\x90\x92\x16\x91\x90\x91\x17\x81U\x91\x01Q\x90\x82\x01U\x01a\x0CtV[a\x0C\xE9a\x12UV[`\x06T`\x01`\xA0\x1B\x90\x04`\xFF\x16\x15a\r6W`\x06\x80T`\x01`\x01`\xA8\x1B\x03\x19\x16\x90U`@Q\x7F\x9A_W\xDE\x85m\xD6h\xC5M\xD9^\\U\xDF\x93C!q\xCB\xCAI\xA8wmV \xEAY\xC0$P\x90`\0\x90\xA1V[`@Qc\xA8c\xAE\xC9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[V[a\rYa\x12UV[a\rO`\0a\x17\xFCV[a\rkaO\xFAV[P`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x84Qa\x01\0\x81\x01\x86R\x81T`\x01`\x01`@\x1B\x03\x80\x82\x16\x83R\x95\x90\x04\x90\x94\x16\x91\x84\x01\x91\x90\x91R`\x01\x81\x01T\x93\x83\x01\x93\x90\x93R`\x02\x83\x01T``\x83\x01R`\x03\x83\x01T`\x80\x83\x01R`\x04\x83\x01T`\xA0\x83\x01R\x82\x01T`\xC0\x82\x01R`\x06\x90\x91\x01T`\xE0\x82\x01R\x90V[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R`\x08\x80T\x90a\x0E\x17`\x01\x83aYgV[\x81T\x81\x10a\x0E'Wa\x0E'aYQV[`\0\x91\x82R` \x90\x91 `\x02\x90\x91\x02\x01T`\x01`\x01`@\x1B\x03\x16\x83\x10a\x0E`W`@Qc\x18V\xA4\x99`\xE2\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0[\x81\x81\x10\x15a\x0E\xF5W\x83`\x08\x82\x81T\x81\x10a\x0E\x7FWa\x0E\x7FaYQV[`\0\x91\x82R` \x90\x91 `\x02\x90\x91\x02\x01T`\x01`\x01`@\x1B\x03\x16\x11\x15a\x0E\xEDW`\x08\x81\x81T\x81\x10a\x0E\xB2Wa\x0E\xB2aYQV[`\0\x91\x82R` \x91\x82\x90 `@\x80Q\x80\x82\x01\x90\x91R`\x02\x90\x92\x02\x01\x80T`\x01`\x01`@\x1B\x03\x16\x82R`\x01\x01T\x91\x81\x01\x91\x90\x91R\x94\x93PPPPV[`\x01\x01a\x0EcV[P`\x08a\x0F\x03`\x01\x83aYgV[\x81T\x81\x10a\x0F\x13Wa\x0F\x13aYQV[`\0\x91\x82R` \x91\x82\x90 `@\x80Q\x80\x82\x01\x90\x91R`\x02\x90\x92\x02\x01\x80T`\x01`\x01`@\x1B\x03\x16\x82R`\x01\x01T\x91\x81\x01\x91\x90\x91R\x93\x92PPPV[`\0\x80a\x0FXa\x18XV[T`\x01`\x01`\xA0\x1B\x03\x16\x92\x91PPV[`\0a\x0Fra\x18|V[\x80T\x90\x91P`\xFF`\x01`@\x1B\x82\x04\x16\x15\x90`\x01`\x01`@\x1B\x03\x16`\0\x81\x15\x80\x15a\x0F\x99WP\x82[\x90P`\0\x82`\x01`\x01`@\x1B\x03\x16`\x01\x14\x80\x15a\x0F\xB5WP0;\x15[\x90P\x81\x15\x80\x15a\x0F\xC3WP\x80\x15[\x15a\x0F\xE1W`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T`\x01`\x01`@\x1B\x03\x19\x16`\x01\x17\x85U\x83\x15a\x10\nW\x84T`\xFF`@\x1B\x19\x16`\x01`@\x1B\x17\x85U[a\x10\x13\x86a\x18\xA0V[a\x10\x1Ba\x18\xB1V[`\0\x80T`\x01` \x1B`\x01``\x1B\x03\x19\x16`\x01`@\x1B\x17\x90Ua\x10>\x88\x88a\x18\xB9V[\x83\x15a\x10\x89W\x84T`\xFF`@\x1B\x19\x16\x85U`@Q\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90a\x10\x80\x90`\x01\x90aW\xFCV[`@Q\x80\x91\x03\x90\xA1[PPPPPPPPV[`\x07\x81\x81T\x81\x10a\x10\xA3W`\0\x80\xFD[`\0\x91\x82R` \x90\x91 \x01T\x90P\x81V[`\x80\x80\x82\x01Q`\xA0\x83\x01Q`\xC0\x84\x01Q`@\x80Q` \x81\x01\x94\x90\x94R\x83\x01\x91\x90\x91R``\x82\x01R`\0\x91\x01`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[`\x08\x81\x81T\x81\x10a\x11\x0CW`\0\x80\xFD[`\0\x91\x82R` \x90\x91 `\x02\x90\x91\x02\x01\x80T`\x01\x90\x91\x01T`\x01`\x01`@\x1B\x03\x90\x91\x16\x91P\x82V[`\x07T`\0\x90C\x84\x11\x80a\x11HWP`\x03\x81\x10[\x15a\x11fW`@Qc\xB0\xB48w`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0\x80\x80a\x11u`\x01\x85aYgV[\x90P[\x81a\x11\xE0W\x86`\x07\x82\x81T\x81\x10a\x11\x91Wa\x11\x91aYQV[\x90`\0R` `\0 \x01T\x11a\x11\xC6W`\x01\x91P`\x07\x81\x81T\x81\x10a\x11\xB8Wa\x11\xB8aYQV[\x90`\0R` `\0 \x01T\x92P[`\x02\x81\x10a\x11\xE0W\x80a\x11\xD8\x81aYzV[\x91PPa\x11xV[\x81a\x11\xFEW`@Qc\xB0\xB48w`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x85a\x12\t\x84\x89aYgV[\x11\x94PPPPP[\x92\x91PPV[a\x12\x1Fa\x12UV[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x12IW`\0`@Qc\x1EO\xBD\xF7`\xE0\x1B\x81R`\x04\x01a\t\xDA\x91\x90aS\xD5V[a\x12R\x81a\x17\xFCV[PV[3a\x12^a\x0FMV[`\x01`\x01`\xA0\x1B\x03\x16\x14a\rOW3`@Qc\x11\x8C\xDA\xA7`\xE0\x1B\x81R`\x04\x01a\t\xDA\x91\x90aS\xD5V[`\0\x80Q` a]\xBC\x839\x81Q\x91R\x81\x10\x80a\x08lW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1B`$\x82\x01Rz\x10\x9B\x8C\x8DM\x0E\x88\x1A[\x9D\x98[\x1AY\x08\x1C\xD8\xD8[\x18\\\x88\x19\x9AY[\x19`*\x1B`D\x82\x01R`d\x01a\t\xDAV[`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x80\x85 \x81Qa\x01\0\x81\x01\x83R\x81T`\x01`\x01`@\x1B\x03\x80\x82\x16\x83R\x96\x90\x04\x90\x95\x16\x92\x85\x01\x92\x90\x92R`\x01\x82\x01T\x90\x84\x01R`\x02\x81\x01T``\x84\x01R`\x03\x81\x01T`\x80\x84\x01R`\x04\x81\x01T`\xA0\x84\x01R\x90\x81\x01T`\xC0\x83\x01R`\x06\x01T`\xE0\x82\x01Ra\x13k\x90a\x10\xB4V[`\x03\x80T`\x01\x90\x81U\x90\x82\x90U`\x04\x80T`\x02U`\0\x80T`\x01`@\x1B\x81\x04c\xFF\xFF\xFF\xFF\x16\x82R`\x05` R`@\x82 `\x06\x01T\x90\x92U\x92\x93P\x90\x91\x90`\x0C\x90a\x13\xC6\x90\x84\x90`\x01``\x1B\x90\x04`\x01`\x01`@\x1B\x03\x16aY\x91V[\x92Pa\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP\x7F\xDB5X%\x9E\x03\x9D~P\xE8\x16\xB9\xDC\xCE0\xFB\x11M\x8A\x9C\x86\xEC\xA5\xAB\x14\xB6\x01\x94\xD6\x94]?`\0`\x0C\x90T\x90a\x01\0\n\x90\x04`\x01`\x01`@\x1B\x03\x16`@Qa\x08B\x91\x90aW\xFCV[`\0a\x148a\x1B\xDCV[`@\x80Q`\x08\x80\x82Ra\x01 \x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x01\0\x806\x837\x01\x90PP\x90P`\x02T\x81`\0\x81Q\x81\x10a\x14wWa\x14waYQV[` \x02` \x01\x01\x81\x81RPP\x83`\0\x01Q`\x01`\x01`@\x1B\x03\x16\x81`\x01\x81Q\x81\x10a\x14\xA4Wa\x14\xA4aYQV[` \x02` \x01\x01\x81\x81RPP\x83` \x01Q`\x01`\x01`@\x1B\x03\x16\x81`\x02\x81Q\x81\x10a\x14\xD1Wa\x14\xD1aYQV[` \x02` \x01\x01\x81\x81RPP\x83`@\x01Q\x81`\x03\x81Q\x81\x10a\x14\xF5Wa\x14\xF5aYQV[` \x02` \x01\x01\x81\x81RPP\x83``\x01Q\x81`\x04\x81Q\x81\x10a\x15\x19Wa\x15\x19aYQV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 `\x03\x01T\x82Q\x90\x91\x83\x91\x81\x10a\x15]Wa\x15]aYQV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x90\x91R`@\x90 `\x04\x01T\x81Q\x82\x90`\x06\x90\x81\x10a\x15\xA1Wa\x15\xA1aYQV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 \x01T\x81Q\x82\x90`\x07\x90\x81\x10a\x15\xE4Wa\x15\xE4aYQV[` \x02` \x01\x01\x81\x81RPPa\x15\xFB\x82\x82\x85a!\xBDV[a\x16\x18W`@Qc\t\xBD\xE39`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[PPPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14\x80a\x16\xA5WP\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16a\x16\x99`\0\x80Q` a]\x9C\x839\x81Q\x91RT`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14\x15[\x15a\rOW`@Qcp>F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x16\xCBa\x12UV[\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x81`@Qa\x08B\x91\x90aS\xD5V[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x17TWP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x17Q\x91\x81\x01\x90aY\xB1V[`\x01[a\x17sW\x81`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\t\xDA\x91\x90aS\xD5V[`\0\x80Q` a]\x9C\x839\x81Q\x91R\x81\x14a\x17\xA4W`@Qc*\x87Ri`\xE2\x1B\x81R`\x04\x81\x01\x82\x90R`$\x01a\t\xDAV[a\x17\xAE\x83\x83a\"\xA8V[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\rOW`@Qcp>F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\x18\x06a\x18XV[\x80T`\x01`\x01`\xA0\x1B\x03\x84\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x93\x94P\x91\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x90V[a\x18\xA8a\"\xFEV[a\x12R\x81a##V[a\rOa\"\xFEV[\x81Q`\x01`\x01`@\x1B\x03\x16\x15\x15\x80a\x18\xDDWP` \x82\x01Q`\x01`\x01`@\x1B\x03\x16\x15\x15[\x80a\x18\xEAWP`\x80\x82\x01Q\x15[\x80a\x18\xF7WP`\xA0\x82\x01Q\x15[\x80a\x19\x04WP`\xC0\x82\x01Q\x15[\x80a\x19\x11WP`\xE0\x82\x01Q\x15[\x80a\x19 WPc\xFF\xFF\xFF\xFF\x81\x16\x15[\x15a\x19>W`@QcP\xDD\x03\xF7`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x81`\x05`\0\x80`\x04\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP\x81`\x05`\0\x80`\x08\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP`\0\x80`\x0Ca\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP\x80`\0\x80a\x01\0\n\x81T\x81c\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83c\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`\0a\x1B$\x83a\x10\xB4V[`\x01\x81\x81U`\xE0\x85\x01Q`\x02\x81\x81U`\x03\x93\x90\x93U`\x04U`\x07\x80T\x80\x83\x01\x82U`\0\x91\x82RC`\0\x80Q` a]\\\x839\x81Q\x91R\x90\x91\x01U`@\x80Q\x80\x82\x01\x82R` \x80\x89\x01Q`\x01`\x01`@\x1B\x03\x90\x81\x16\x83R\x92\x90\x98\x01Q\x97\x81\x01\x97\x88R`\x08\x80T\x94\x85\x01\x81U\x90\x92R\x90Q`\0\x80Q` a]\xDC\x839\x81Q\x91R\x92\x90\x93\x02\x91\x82\x01\x80T`\x01`\x01`@\x1B\x03\x19\x16\x93\x90\x91\x16\x92\x90\x92\x17\x90\x91U\x92Q`\0\x80Q` a^\x1C\x839\x81Q\x91R\x90\x93\x01\x92\x90\x92UPPV[a\x1B\xE4aPrV[b\x01\0\0\x81R`\x08` \x82\x01R\x7F\x01=\x1DKBQy%\x8BWx`9yU\xCB\xFA\x08\x16\xE3+\x1C%\xA1\xFDsL\x91\xB9Q\xEE\x81`@\x82\x01QR\x7F\x16\xB8\x8D\xC7C\x9Am\x84\x1E\x1A\x11\x03\xF5\xA3\xD2\xD2D\x017\xF1\x8D\x02v5\x03\xBA\xC7\xB4]\xCB\x98;` `@\x83\x01Q\x01R\x7F\x0C<\x86O\x19_Y\x11\x99'\xF58W\xF1\xDE\x8B\xF5u\x94\x17H\xB755\x1F\xD3\x13s\xC7\x87\\-``\x82\x01QR\x7F\x16\x9B\xA1Q\x07\xF2\xEF\xF9\xB94\x1B\xF3\x07B\xA88\xD2}\xBDi\xE8\x8B#S\xDC\xA8Y/\x15\xF1\x11\x1C` ``\x83\x01Q\x01R\x7F\x11\xD4\xCE\xB1Ya\xD1\x0BaV\xAE=\t\xBBx\xB4\xDFE\xFB\x85C\x06\x08\x84\xE7\xD4\0u[\xEBJ\xC8`\x80\x82\x01QR\x7F\x03&\xFF\x069\x1E\xD5\xD2n\xC1\xBC\x08\x0B\x8DF\x01N\xE2,\x0Ch\xED\x02/\x16 \xC4\xD9\xD3\x847\xD3` `\x80\x83\x01Q\x01R\x7F#a\x0C\xB4>!\x03<6\x8A\x93b-\xD4\x05\xB9\x05\xA0\xEB4L\x98\xB9\xD7\xCF\x08\xB0\xC5\xEB\xF7\xC89`\xA0\x82\x01QR~\x13y4*Mw\xD4p\x87C\xAF\xF0\x1F\xF2z\xA1\x19\x17G\x8F\xDC\x8E+}F0\x81sWr\xEA` `\xA0\x83\x01Q\x01R\x7F\x19M\xAF\x85\xD9\xEE\xD9\x93{(\xE2\xA6\x80\xFC\xC5\xA7i\"\xC1\\\xD3\x1D\xC4\xF6\0\xE1I9\xB8 \x0C\xE7`\xC0\x82\x01QR\x7F%(\x0B\x12F$\x91\x1C\x7F\x87\xB4\xC2\xD8\x7FY\xC6\xC0~>\xEE\xB1\raM\xA2\x16\xF6!\x9F\xFEP\xB6` `\xC0\x83\x01Q\x01R\x7F\x04\x88.\xF3\x98\x99\xEA8\xC9gzH\xB8\xF8\xCCjg(N\x17\xFF\x94\x02\x89\xFA\xAA5\x9E\xEC\x9B3\xA6`\xE0\x82\x01QR\x7F\x1B\xAE\x9F6\xE6\x19\x078\xC7\x11P\x1B\xE5?)\x9B\xF6\x13H\xE6\x1E.\xF9\xD5wv\x0Ed\xF6)6\x8D` `\xE0\x83\x01Q\x01R\x7F-\x81\r0\x12\x0C\xB9>\x1A%K\x89\xED\n\xE8lv\x1FI\xB4\xF1)E\x9C\xD54\xF9U\x18Q5\x0Fa\x01\0\x82\x01QR\x7F\x0B%9M\xA5\xA1\xD45\xDA\xCC\xC2\xEA\xDD\x03\x9E,'\t\xF5\xF4/\xAB\xD9\xAF\xBA\x81^\xD6-j\xF3k` a\x01\0\x83\x01Q\x01R\x7F\x1C,\xE7\xBEW\x0B\xEA\x9EC\xF3\xD3\xD7\xCB\xCA\x84\xBD\xB4\xFC\x89\xB5:\xE6WS\x1D\xE7&p\xA6\x10^\x08a\x01 \x82\x01QR\x7F\x1A\x02U\xEC\x8C|\x87i3[\xC9\xDCk\"*\xC6\xA0Nvm\x08\xB4\\\x8C\xC5sY,\x05\xBC\x9C\x14` a\x01 \x83\x01Q\x01R\x7F\x1C\x16AY\x13k\x8F[Gs\xE13\xF4\x83\xA8\xA1\x92\xAB\x15\xD6\xD3\xEE\x01/\x17\x1B=\x02\xFDE\x06\xE7a\x01@\x82\x01QR\x7F'.\xB7\xD63\xCE\xDBh\xCE\x01\x13\xF4B\n\xB5a\x0B\x81\xB8\xBA\x1A\xB94\x8D\xB1Wa\xD4\x0E\x8D\xF5\xBA` a\x01@\x83\x01Q\x01R\x7F\x0EDf9\xAAl\xAF%\xE9>\xF7G\x08N9\xB8\xEA\x90\xAB\xF2;\xB4\x8C(\xFD_\x9B\xA7\xBAeP\"a\x01`\x82\x01QR\x7F\x03>\x19Z\x9E\xA3\xA9\xCE@\xB7+g:\xFBEDL\xA1\xB1_\x05C\xF4M\x10\xF5\xC6@\xA7\x80go` a\x01`\x83\x01Q\x01R\x7F\x0E\x8D\xB2\xB2\x89=\xF2=\xD6\x81y\x96\xF7_\x10\0\x9D\x99\"\x07\x93\xECsa\x01\xC0\x82\x01QR\x7F\x19\xEB\x12\xA7\x82|\r\xDFc\x83\xFE\x80l9S\xBD\x06\xB0\x8A\xAE{\xF2\xA0\x1FU\xC9\x86\xA8OP\xCC(` a\x01\xC0\x83\x01Q\x01R\x7F\x01V\x910\x88F\xE6\x8E\xA8V\xA2\xCB$\xC9\x90?\x0C\x86\x05\xDE\xA1\x90\x82\x91\x80\xFFk\xDD\x1Ce\x08\x03a\x01\xE0\x82\x01QR\x7F\x1F\xFDx\x9B\x15[\x8A\xCB\x13\xE0\xF6\xA4\x8BP\xF7\xAA\x80\x92T\x08\x88\xD0\t\x14\x10W\xD4V\x90\x91X$` a\x01\xE0\x83\x01Q\x01R\x7F\x05E\xACz\xA6m\xCF7\x19\x98\x848\xC8\x06\xFCbM\xE5z\xB4?\x85\x809/\x88\xC8l\x13x\xCEJa\x02\0\x82\x01QR\x7F\x16\xB7\xF2P\x84.\xCFN6\x90pj\x1E\x15-zW\xF7\x0FUo\x92\x07m\xA7\x85\xFD\xD3c\xC1\x9F\xCF` a\x02\0\x83\x01Q\x01R\x7F \xCB\x7F\xF3Z\x83\xA7\xDC1@6\xE4p\xF1L0\xFB\x0E\x98\xD3]f;$;\",\xAAo\xC7\xDBDa\x02 \x82\x01QR\x7F\x14\x9FAWDpth\xBD\xAAN\x85E \x1A\xB4\r\x191\xA7\xD3\x1F#v\x8F\xA7\xC6Ut\xEE>\xAB` a\x02 \x83\x01Q\x01R\x7F\n%\xC1\xB7W9\x06\xDCN\x19;N\xA8/\xD1\xFE|\xCE\xBCM\x92]\xAD&\xF0\xFF\t\xC8L\x9F\x1Aua\x02@\x82\x01QR\x7F\nR\x1F\xF3\x0C\x8F6fy\x8F\x84|]L7\x96X\xFB\xA1\x01V\xE7\xA9I\x9F'\x13\xFA\xE9\xBF+\xE1` a\x02@\x83\x01Q\x01R\x7F\x03\xDBe\x10\xC3\xF16)\xFD\xED\x9AZ-AeK\xBC\xE4\xEFm\x02L\xADS\x10\0Q\xD4\xA3\xF3\xEB\xC9a\x02`\x82\x01QR\x7F\x08\xE8\n\\\x8EL\x9B\x9F&\xF3\0<\xC5\x94\x03\xA1\x8D16\xAF\xD00\x86\x8D%\xCC\x8B\x80~*\xB3p` a\x02`\x83\x01Q\x01R\x90V[`\0a!\xC8\x82a#+V[a!\xEB\x83`\0\x81Q\x81\x10a!\xDEWa!\xDEaYQV[` \x02` \x01\x01Qa\x12\x87V[a\"\x01\x83`\x01\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"\x17\x83`\x02\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"-\x83`\x03\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"C\x83`\x04\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"Y\x83`\x05\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"o\x83`\x06\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"\x85\x83`\x07\x81Q\x81\x10a!\xDEWa!\xDEaYQV[`\0a\"\x92\x85\x85\x85a$cV[\x90Pa\"\x9D\x81a%\xADV[\x91PP[\x93\x92PPPV[a\"\xB1\x82a*\x0CV[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a\"\xF6Wa\x17\xAE\x82\x82a*hV[a\x08la*\xDEV[a#\x06a*\xFDV[a\rOW`@Qc\x1A\xFC\xD7\x9F`\xE3\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x12\x1Fa\"\xFEV[\x80Qa#6\x90a+\x17V[a#C\x81` \x01Qa+\x17V[a#P\x81`@\x01Qa+\x17V[a#]\x81``\x01Qa+\x17V[a#j\x81`\x80\x01Qa+\x17V[a#w\x81`\xA0\x01Qa+\x17V[a#\x84\x81`\xC0\x01Qa+\x17V[a#\x91\x81`\xE0\x01Qa+\x17V[a#\x9F\x81a\x01\0\x01Qa+\x17V[a#\xAD\x81a\x01 \x01Qa+\x17V[a#\xBB\x81a\x01@\x01Qa+\x17V[a#\xC9\x81a\x01`\x01Qa+\x17V[a#\xD7\x81a\x01\x80\x01Qa+\x17V[a#\xE5\x81a\x01\xA0\x01Qa\x12\x87V[a#\xF3\x81a\x01\xC0\x01Qa\x12\x87V[a$\x01\x81a\x01\xE0\x01Qa\x12\x87V[a$\x0F\x81a\x02\0\x01Qa\x12\x87V[a$\x1D\x81a\x02 \x01Qa\x12\x87V[a$+\x81a\x02@\x01Qa\x12\x87V[a$9\x81a\x02`\x01Qa\x12\x87V[a$G\x81a\x02\x80\x01Qa\x12\x87V[a$U\x81a\x02\xA0\x01Qa\x12\x87V[a\x12R\x81a\x02\xC0\x01Qa\x12\x87V[a$kaQvV[\x83` \x01Q\x83Q\x14a$\x90W`@Qc \xFA\x9D\x89`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a$\x9D\x85\x85\x85a+\xA5V[\x90P`\0a$\xAE\x86`\0\x01Qa.\xC4V[\x90P`\0a$\xC1\x82\x84`\xA0\x01Q\x88a2\x8FV[`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x03\xC0\x806\x837PP`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[a%\x0CaQ\xB0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a%\x04W\x90PP\x90P`\0a%1\x8A\x85\x8A\x89\x87\x87a2\xEFV[`\xA0\x87\x01Q``\x87\x01Q\x91\x92P\x90`\0\x80Q` a]\xBC\x839\x81Q\x91R`\0\x81\x83\x85\t`@\x80Qa\x01\0\x81\x01\x82R`\xE0\x9C\x8D\x01Q\x81R` \x81\x01\x96\x90\x96R\x85\x01RPPP``\x81\x01\x91\x90\x91R`\x80\x81\x01\x92\x90\x92R`\xA0\x82\x01Ra\x01`\x86\x01Q`\xC0\x82\x01Ra\x01\x80\x90\x95\x01Q\x92\x85\x01\x92\x90\x92RP\x91\x94\x93PPPPV[`\0`\0\x80Q` a]\xBC\x839\x81Q\x91Ra%\xC6aQ\xB0V[a%\xCEaQ\xB0V[`@\x80Q`\x02\x80\x82R``\x82\x01\x83R`\0\x92` \x83\x01\x90\x806\x837PP`@\x80Q`\x02\x80\x82R``\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[a&\x10aQ\xB0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a&\x08W\x90PP\x90P`\0`\x01\x90P\x80\x83`\0\x81Q\x81\x10a&?Wa&?aYQV[` \x02` \x01\x01\x81\x81RPP\x87`\xC0\x01Q\x82`\0\x81Q\x81\x10a&cWa&caYQV[` \x02` \x01\x01\x81\x90RP\x87`\0\x01Q\x83`\x01\x81Q\x81\x10a&\x86Wa&\x86aYQV[` \x02` \x01\x01\x81\x81RPP\x87`\xE0\x01Q\x82`\x01\x81Q\x81\x10a&\xAAWa&\xAAaYQV[` \x02` \x01\x01\x81\x90RPa&\xBF\x82\x84a3$V[`\x80\x89\x01QQ\x90\x95P``\x93P\x83\x92P\x90P`\0a&\xDE\x82`\x02aY\xCAV[a&\xE9\x90`\x01aY\xCAV[\x90P\x80`\x01`\x01`@\x1B\x03\x81\x11\x15a'\x03Wa'\x03aRsV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a',W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x80`\x01`\x01`@\x1B\x03\x81\x11\x15a'GWa'GaRsV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a'\x80W\x81` \x01[a'maQ\xB0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a'eW\x90P[P\x92PPP`\0\x80`\0[\x89`\x80\x01QQ\x81\x10\x15a($W\x89`\x80\x01Q\x81\x81Q\x81\x10a'\xAEWa'\xAEaYQV[` \x02` \x01\x01Q\x85\x83\x81Q\x81\x10a'\xC8Wa'\xC8aYQV[` \x02` \x01\x01\x81\x81RPP\x89`\xA0\x01Q\x81\x81Q\x81\x10a'\xEAWa'\xEAaYQV[` \x02` \x01\x01Q\x84\x83\x81Q\x81\x10a(\x04Wa(\x04aYQV[` \x90\x81\x02\x91\x90\x91\x01\x01Ra(\x1A`\x01\x83aY\xCAV[\x91P`\x01\x01a'\x8BV[P\x88` \x01Q\x84\x82\x81Q\x81\x10a(a*\xC5V[``\x91P[P\x91P\x91Pa*\xD5\x85\x83\x83a6\x86V[\x95\x94PPPPPV[4\x15a\rOW`@Qc\xB3\x98\x97\x9F`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a+\x07a\x18|V[T`\x01`@\x1B\x90\x04`\xFF\x16\x91\x90PV[`\0`\0\x80Q` a]<\x839\x81Q\x91Ra+1\x83a6\xD9V[\x15a+;WPPPV[\x82Q` \x84\x01Q\x82`\x03\x84\x85\x85\x86\t\x85\t\x08\x83\x82\x83\t\x14\x83\x82\x10\x84\x84\x10\x16\x16\x93PPP\x81a\x17\xAEW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x17`$\x82\x01Rv\x10\x9B\x8C\x8DM\x0E\x88\x1A[\x9D\x98[\x1AY\x08\x11\xCCH\x1C\x1B\xDA[\x9D`J\x1B`D\x82\x01R`d\x01a\t\xDAV[a+\xED`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@\x80Q\x80\x82\x01\x90\x91R``\x81R`\0` \x82\x01R`\0\x80Q` a]\xBC\x839\x81Q\x91Ra,\x1C\x82\x87\x87a6\xE8V[\x81Q\x84Qa,)\x90a:\x81V[a,6\x86` \x01Qa:\x81V[a,C\x87`@\x01Qa:\x81V[a,P\x88``\x01Qa:\x81V[a,]\x89`\x80\x01Qa:\x81V[`@Q` \x01a,r\x96\x95\x94\x93\x92\x91\x90aY\xF9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\x8D\x82a:\xF6V[Pa,\x97\x82a:\xF6V[``\x84\x01Ra,\xA5\x82a:\xF6V[`\x80\x84\x01R\x81Q`\xA0\x85\x01Qa,\xBA\x90a:\x81V[`@Q` \x01a,\xCB\x92\x91\x90aZxV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\xE6\x82a:\xF6V[\x83R\x81Q`\xC0\x85\x01Qa,\xF8\x90a:\x81V[a-\x05\x86`\xE0\x01Qa:\x81V[a-\x13\x87a\x01\0\x01Qa:\x81V[a-!\x88a\x01 \x01Qa:\x81V[a-/\x89a\x01@\x01Qa:\x81V[`@Q` \x01a-D\x96\x95\x94\x93\x92\x91\x90aY\xF9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra-_\x82a:\xF6V[`\xA0\x84\x01R\x81Qa\x01\xA0\x85\x01Qa-u\x90a;XV[a-\x83\x86a\x01\xC0\x01Qa;XV[a-\x91\x87a\x01\xE0\x01Qa;XV[a-\x9F\x88a\x02\0\x01Qa;XV[a-\xAD\x89a\x02 \x01Qa;XV[`@Q` \x01a-\xC2\x96\x95\x94\x93\x92\x91\x90aZ\xA7V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x83Ra\x02@\x85\x01Qa-\xE4\x90a;XV[a-\xF2\x86a\x02`\x01Qa;XV[a.\0\x87a\x02\x80\x01Qa;XV[a.\x0E\x88a\x02\xA0\x01Qa;XV[a.\x1C\x89a\x02\xC0\x01Qa;XV[`@Q` \x01a.1\x96\x95\x94\x93\x92\x91\x90aZ\xA7V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra.L\x82a:\xF6V[`\xC0\x84\x01R\x81Qa\x01`\x85\x01Qa.b\x90a:\x81V[a.p\x86a\x01\x80\x01Qa:\x81V[`@Q` \x01a.\x82\x93\x92\x91\x90aZ\xE3V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra.\x9D\x82a:\xF6V[`\xE0\x84\x01R\x82Q\x81\x81\x80\t\x82\x82\x82\t` \x86\x01\x91\x90\x91R`@\x85\x01RP\x91\x95\x94PPPPPV[a.\xF6`@Q\x80`\xA0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[\x81b\x01\0\0\x03a/\x8AWP`@\x80Q`\xA0\x81\x01\x82R`\x10\x81R` \x81\x01\x92\x90\x92R\x7F0d\x1E\x0E\x92\xBE\xBE\xF8\x18&\x8Df;\xCA\xD6\xDB\xCF\xD6\xC0\x14\x91p\xF6\xD7\xD3P\xB1\xB1\xFAl\x10\x01\x90\x82\x01R~\xEE\xB2\xCBY\x81\xEDEd\x9A\xBE\xBD\xE0\x81\xDC\xFF\x16\xC8`\x1D\xE44~}\xD1b\x8B\xA2\xDA\xACC\xB7``\x82\x01R\x7F\x0B]V\xB7\x7F\xE7\x04\xE8\xE9#8\xC0\x08/7\xE0\x91\x12d\x14\xC80\xE4\xC6\x92-Z\xC8\x02\xD8B\xD4`\x80\x82\x01R\x90V[\x81b\x02\0\0\x03a0\x1FWP`@\x80Q`\xA0\x81\x01\x82R`\x11\x81R` \x81\x01\x92\x90\x92R\x7F0d6@\xB9\xF8/\x90\xE8;i\x8E^\xA6\x17\x9C|\x05T.\x85\x953\xB4\x8B\x99S\xA2\xF56\x08\x01\x90\x82\x01R\x7F\x1B\xF8-\xEB\xA7\xD7I\x02\xC3p\x8C\xC6\xE7\x0Ea\xF3\x05\x12\xEC\xA9VU!\x0E'nXX\xCE\x8FX\xE5``\x82\x01R\x7F$L\xF0\x10\xC4<\xA8r7\xD8\xB0\x0B\xF9\xDDP\xC4\xC0\x1C\x7F\x08k\xD4\xE8\xC9 \xE7RQ\xD9o\r\"`\x80\x82\x01R\x90V[\x81b\x04\0\0\x03a0\xB4WP`@\x80Q`\xA0\x81\x01\x82R`\x12\x81R` \x81\x01\x92\x90\x92R\x7F0dBY\xCD\x94\xE7\xDDPE\xD7\xA2p\x13\xB7\xFC\xD2\x1C\x9E;\x7F\xA7R\"\xE7\xBD\xA4\x9Br\x9B\x04\x01\x90\x82\x01R\x7F\x19\xDD\xBC\xAF:\x8DF\xC1\\\x01v\xFB\xB5\xB9^M\xC5p\x88\xFF\x13\xF4\xD1\xBD\x84\xC6\xBF\xA5}\xCD\xC0\xE0``\x82\x01R\x7F\x03hS\xF0\x83x\x0E\x87\xF8\xD7\xC7\x1D\x11\x11\x19\xC5}\xBE\x11\x8C\"\xD5\xADpz\x821tf\xC5\x17L`\x80\x82\x01R\x90V[\x81b\x08\0\0\x03a1IWP`@\x80Q`\xA0\x81\x01\x82R`\x13\x81R` \x81\x01\x92\x90\x92R\x7F0dHfWcD\x03\x84K\x0E\xACx\xCA\x88,\xFD(CA\xFC\xB0aZ\x15\xCF\xCD\x17\xB1M\x82\x01\x90\x82\x01R\x7F\"`\xE7$\x84K\xCARQ\x82\x93S\x96\x8EI\x150RXA\x83WG:\\\x1DY\x7Fa?l\xBD``\x82\x01R\x7F\x06\xE4\x02\xC0\xA3\x14\xFBg\xA1\\\xF8\x06fJ\xE1\xB7\"\xDB\xC0\xEF\xE6nl\x81\xD9\x8F\x99$\xCASS!`\x80\x82\x01R\x90V[\x81b\x10\0\0\x03a1\xDEWP`@\x80Q`\xA0\x81\x01\x82R`\x14\x81R` \x81\x01\x92\x90\x92R\x7F0dKl\x9CJr\x16\x9EM\xAA1}%\xF0E\x12\xAE\x15\xC5;4\xE8\xF5\xAC\xD8\xE1U\xD0\xA6\xC1\x01\x90\x82\x01R\x7F&\x12]\xA1\n\x0E\xD0c'P\x8A\xBA\x06\xD1\xE3\x03\xACaf2\xDB\xED4\x9FSB-\xA9S3xW``\x82\x01R\x7F\x10\x0C3-!\0\x89_\xABds\xBC,Q\xBF\xCAR\x1FE\xCB;\xAC\xA6&\x08R\xA8\xFD\xE2l\x91\xF3`\x80\x82\x01R\x90V[\x81` \x03a2qWP`@\x80Q`\xA0\x81\x01\x82R`\x05\x81R` \x81\x01\x92\x90\x92R\x7F.\xE1+\xFFJ(\x13(j\x8D\xC3\x88\xCDuM\x9A>\xF2I\x065\xEB\xA5\x0C\xB9\xC2\xE5\xE7P\x80\0\x01\x90\x82\x01R\x7F\t\xC52\xC60k\x93\xD2\x96x \rG\xC0\xB2\xA9\x9C\x18\xD5\x1B\x83\x8E\xEB\x1D>\xEDLS;\xB5\x12\xD0``\x82\x01R\x7F'$q6\x03\xBF\xBDy\n\xEA\xF3\xE7\xDF%\xD8\xE7\xEF\x8F1\x134\x90[M\x8C\x99\x98\x0C\xF2\x10\x97\x9D`\x80\x82\x01R\x90V[`@Qc\xE2\xEF\t\xE5`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x91\x90PV[a2\xB3`@Q\x80``\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[a2\xBD\x84\x84a<\x8FV[\x80\x82Ra2\xCD\x90\x85\x90\x85\x90a<\xE3V[` \x82\x01R\x80Qa2\xE3\x90\x85\x90\x84\x90\x86\x90a=WV[`@\x82\x01R\x93\x92PPPV[`\0\x80a2\xFD\x85\x87\x89a?\x0BV[\x90Pa3\r\x88\x86\x89\x89\x88\x88a?\xF7V[a3\x18\x81\x87\x86aC\x03V[\x98\x97PPPPPPPPV[a3,aQ\xB0V[\x82Q\x82Q\x14a3}W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FMSM error: length does not match`D\x82\x01R`d\x01a\t\xDAV[a3\xBB\x83`\0\x81Q\x81\x10a3\x93Wa3\x93aYQV[` \x02` \x01\x01Q\x83`\0\x81Q\x81\x10a3\xAEWa3\xAEaYQV[` \x02` \x01\x01QaCSV[\x90P`\x01[\x82Q\x81\x10\x15a4\x0BWa4\x01\x82a3\xFC\x86\x84\x81Q\x81\x10a3\xE2Wa3\xE2aYQV[` \x02` \x01\x01Q\x86\x85\x81Q\x81\x10a3\xAEWa3\xAEaYQV[aC\xE7V[\x91P`\x01\x01a3\xC0V[P\x92\x91PPV[`\0a4,`\0\x80Q` a]\xBC\x839\x81Q\x91R\x83a[\x0EWa>\x0EaRsV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a>7W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P\x88\x8B\x85\t\x93P`\x01\x92P`\0[\x88\x81\x10\x15a>|W` \x81\x02` \x84\x01\x01Q\x95P\x89\x8D\x87\x8C\x03\x08\x96P\x89\x87\x85\t` \x82\x81\x02\x84\x01\x01\x88\x90R\x93P`\x01\x01a>GV[Pa>\x86\x83aE\xB8V[\x92P`\0[\x88\x81\x10\x15a>\xF9W` \x81\x02` \x84\x01\x01Q\x95P\x89\x86\x86\t\x97P\x89\x84\x89\t\x97P`\0[\x89\x81\x10\x15a>\xD8W\x80\x82\x14a>\xD0W` \x81\x02` \x84\x01\x01Q\x97P\x8A\x88\x8A\t\x98P[`\x01\x01a>\xAEV[P` \x81\x02` \x8F\x01\x01Q\x95P\x89\x86\x89\t\x97P\x89\x88\x8C\x08\x9AP`\x01\x01a>\x8BV[PPPPPPPPPP\x94\x93PPPPV[`\0\x80`\0\x80Q` a]\xBC\x839\x81Q\x91R\x90P`\0\x83` \x01Q\x90P`\0\x84`@\x01Q\x90P`\0`\x01\x90P``\x88\x01Q`\x80\x89\x01Qa\x01\xA0\x89\x01Qa\x02@\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xC0\x89\x01Qa\x02`\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xE0\x89\x01Qa\x02\x80\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02\0\x89\x01Qa\x02\xA0\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02 \x89\x01Q\x91Pa\x02\xC0\x89\x01Q\x86\x87\x82\x89\x85\x87\x08\t\x85\t\x93PPPP\x87Q` \x89\x01Q\x85\x86\x86\x83\t\x87\x03\x85\x08\x96PP\x84\x85\x83\x83\t\x86\x03\x87\x08\x99\x98PPPPPPPPPV[a@\x05\x86\x86\x86\x86\x85\x87aG#V[`\xC0\x85\x01Q\x82Q`\0\x80Q` a]\xBC\x839\x81Q\x91R\x91\x90\x81\x90\x81\x90\x86\x90`\x14\x90\x81\x10a@4Wa@4aYQV[` \x02` \x01\x01\x81\x81RPP\x85`\0\x01Q\x84`\x14\x81Q\x81\x10a@XWa@XaYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x15\x81Q\x81\x10a@}Wa@}aYQV[` \x02` \x01\x01\x81\x81RPP\x85` \x01Q\x84`\x15\x81Q\x81\x10a@\xA1Wa@\xA1aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x16\x81Q\x81\x10a@\xC6Wa@\xC6aYQV[` \x02` \x01\x01\x81\x81RPP\x85`@\x01Q\x84`\x16\x81Q\x81\x10a@\xEAWa@\xEAaYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x17\x81Q\x81\x10aA\x0FWaA\x0FaYQV[` \x02` \x01\x01\x81\x81RPP\x85``\x01Q\x84`\x17\x81Q\x81\x10aA3WaA3aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x18\x81Q\x81\x10aAXWaAXaYQV[` \x02` \x01\x01\x81\x81RPP\x85`\x80\x01Q\x84`\x18\x81Q\x81\x10aA|WaA|aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x19\x81Q\x81\x10aA\xA1WaA\xA1aYQV[` \x02` \x01\x01\x81\x81RPP\x88`@\x01Q\x84`\x19\x81Q\x81\x10aA\xC5WaA\xC5aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1A\x81Q\x81\x10aA\xEAWaA\xEAaYQV[` \x02` \x01\x01\x81\x81RPP\x88``\x01Q\x84`\x1A\x81Q\x81\x10aB\x0EWaB\x0EaYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1B\x81Q\x81\x10aB3WaB3aYQV[` \x02` \x01\x01\x81\x81RPP\x88`\x80\x01Q\x84`\x1B\x81Q\x81\x10aBWWaBWaYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1C\x81Q\x81\x10aB|WaB|aYQV[` \x02` \x01\x01\x81\x81RPP\x88`\xA0\x01Q\x84`\x1C\x81Q\x81\x10aB\xA0WaB\xA0aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x87`\xE0\x01Q\x85`\x1D\x81Q\x81\x10aB\xC9WaB\xC9aYQV[` \x02` \x01\x01\x81\x81RPP\x85`\xA0\x01Q\x84`\x1D\x81Q\x81\x10aB\xEDWaB\xEDaYQV[` \x02` \x01\x01\x81\x90RPPPPPPPPPPV[`\0\x80Q` a]\xBC\x839\x81Q\x91R\x83\x81\x03\x90`\0[`\n\x81\x10\x15aCJW` `\x15\x82\x01\x02\x84\x01Q` \x82\x02a\x01\xA0\x01\x86\x01Q\x83\x84\x82\x84\t\x86\x08\x94PPP`\x01\x01aC\x19V[PP\x93\x92PPPV[aC[aQ\xB0V[aCcaQ\xCAV[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x81\x01\x83\x90R`\0``\x83`\x80\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80aC\x95W`\0\x80\xFD[P\x80aC\xDFW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01RxBn254: scalar mul failed!`8\x1B`D\x82\x01R`d\x01a\t\xDAV[PP\x92\x91PPV[aC\xEFaQ\xB0V[aC\xF7aQ\xE8V[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x83\x01R\x83\x01Q``\x80\x83\x01\x91\x90\x91R`\0\x90\x83`\xC0\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80aD4W`\0\x80\xFD[P\x80aC\xDFW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: group addition failed!\0\0\0`D\x82\x01R`d\x01a\t\xDAV[\x80Q\x15aD\x92W\x80Q\x80\x82` \x01\xFD[`@Qc\n\x12\xF5!`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x81aD\xB9\x81`\x1FaY\xCAV[\x10\x15aD\xF8W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x0E`$\x82\x01Rmslice_overflow`\x90\x1B`D\x82\x01R`d\x01a\t\xDAV[aE\x02\x82\x84aY\xCAV[\x84Q\x10\x15aEFW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x11`$\x82\x01Rpslice_outOfBounds`x\x1B`D\x82\x01R`d\x01a\t\xDAV[``\x82\x15\x80\x15aEeW`@Q\x91P`\0\x82R` \x82\x01`@RaE\xAFV[`@Q\x91P`\x1F\x84\x16\x80\x15` \x02\x81\x84\x01\x01\x85\x81\x01\x87\x83\x15` \x02\x84\x8B\x01\x01\x01[\x81\x83\x10\x15aE\x9EW\x80Q\x83R` \x92\x83\x01\x92\x01aE\x86V[PP\x85\x84R`\x1F\x01`\x1F\x19\x16`@RP[P\x94\x93PPPPV[`\0\x80`\0`\0\x80Q` a]\xBC\x839\x81Q\x91R\x90P`@Q` \x81R` \x80\x82\x01R` `@\x82\x01R\x84``\x82\x01R`\x02\x82\x03`\x80\x82\x01R\x81`\xA0\x82\x01R` `\0`\xC0\x83`\x05Z\xFA\x92PP`\0Q\x92P\x81aFWW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: pow precompile failed!\0\0\0`D\x82\x01R`d\x01a\t\xDAV[PP\x91\x90PV[``\x82` \x01Q\x82\x11\x15aF\x85W`@Qc\x8C^\x11\xF1`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x83\x01Q`\x01`\0\x80Q` a]\xBC\x839\x81Q\x91R\x84`\x01`\x01`@\x1B\x03\x81\x11\x15aF\xB3WaF\xB3aRsV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15aF\xDCW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x84\x15\x19\x15a<\xDAW` \x84\x01\x85` \x02\x81\x01`\x01\x82R` \x82\x01\x91P[\x80\x82\x10\x15aG\x18W\x82\x85\x85\t\x93P\x83\x82R` \x82\x01\x91PaF\xFCV[PPPPP\x92\x91PPV[`\0\x80`\0\x80`\0\x80`\0\x80Q` a]\xBC\x839\x81Q\x91R\x90P\x80` \x8B\x01Q` \x8D\x01Q\t\x95P\x8AQ\x93P\x80`\xA0\x8C\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` a^\\\x839\x81Q\x91R\x84\t\x91P\x80a\x01\xC0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` a]\xFC\x839\x81Q\x91R\x84\t\x91P\x80a\x01\xE0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` a^<\x839\x81Q\x91R\x84\t\x91P\x80a\x02\0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` a]|\x839\x81Q\x91R\x84\t\x91P\x80a\x02 \x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80\x84\x87\x08\x95P\x88`\xA0\x01Q\x88`\0\x81Q\x81\x10aHRWaHRaYQV[` \x02` \x01\x01\x81\x90RP\x85\x87`\0\x81Q\x81\x10aHqWaHqaYQV[` \x02` \x01\x01\x81\x81RPP\x80``\x8C\x01Q\x8CQ\t\x94P\x80a\x02\xC0\x8A\x01Q\x86\t\x94P\x80a\x02@\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02`\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xC0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02\x80\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xE0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02\xA0\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x02\0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x8B`\xC0\x01Q\x88`\x01\x81Q\x81\x10aISWaISaYQV[` \x90\x81\x02\x91\x90\x91\x01\x01RaIh\x85\x82aYgV[\x87`\x01\x81Q\x81\x10aI{WaI{aYQV[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xA0\x01Q\x87`\x02\x81Q\x81\x10aI\xA0WaI\xA0aYQV[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xC0\x01Q\x87`\x03\x81Q\x81\x10aI\xC5WaI\xC5aYQV[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xE0\x01Q\x87`\x04\x81Q\x81\x10aI\xEAWaI\xEAaYQV[` \x02` \x01\x01\x81\x81RPP\x88a\x02\0\x01Q\x87`\x05\x81Q\x81\x10aJ\x0FWaJ\x0FaYQV[` \x02` \x01\x01\x81\x81RPP\x8B`\xE0\x01Q\x88`\x02\x81Q\x81\x10aJ3WaJ3aYQV[` \x02` \x01\x01\x81\x90RP\x8Ba\x01\0\x01Q\x88`\x03\x81Q\x81\x10aJWWaJWaYQV[` \x02` \x01\x01\x81\x90RP\x8Ba\x01 \x01Q\x88`\x04\x81Q\x81\x10aJ{WaJ{aYQV[` \x02` \x01\x01\x81\x90RP\x8Ba\x01@\x01Q\x88`\x05\x81Q\x81\x10aJ\x9FWaJ\x9FaYQV[` \x02` \x01\x01\x81\x90RP\x80a\x01\xC0\x8A\x01Qa\x01\xA0\x8B\x01Q\t\x92P\x82\x87`\x06\x81Q\x81\x10aJ\xCEWaJ\xCEaYQV[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01`\x01Q\x88`\x06\x81Q\x81\x10aJ\xF3WaJ\xF3aYQV[` \x02` \x01\x01\x81\x90RP\x80a\x02\0\x8A\x01Qa\x01\xE0\x8B\x01Q\t\x92P\x82\x87`\x07\x81Q\x81\x10aK\"WaK\"aYQV[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\x80\x01Q\x88`\x07\x81Q\x81\x10aKGWaKGaYQV[` \x02` \x01\x01\x81\x90RPa\x01\xA0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\x08\x81Q\x81\x10aK\x80WaK\x80aYQV[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\xE0\x01Q\x88`\x08\x81Q\x81\x10aK\xA5WaK\xA5aYQV[` \x02` \x01\x01\x81\x90RPa\x01\xC0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\t\x81Q\x81\x10aK\xDEWaK\xDEaYQV[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02\0\x01Q\x88`\t\x81Q\x81\x10aL\x03WaL\x03aYQV[` \x02` \x01\x01\x81\x90RPa\x01\xE0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\n\x81Q\x81\x10aL v\xCC75\xA9 \xA3\xCAP]8+\xBC0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE3\x1E\xE6x\xA0G\nu\xA6\xEA\xA8\xFE\x83p`I\x8B\xA8(\xA3p;1\x1D\x0Fw\xF0\x10BJ\xFE\xB0%\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE4 B\xA5\x87\xA9\x0C\x18{\n\x08|\x03\xE2\x9C\x96\x8B\x95\x0B\x1D\xB2m\\\x82\xD6f\x90Zh\x95y\x0C\n/\x8D\xD1\xF1\xA7X v\xCC75\xA9 \xA3\xCAP]8+\xBC\x90V[a\x0C\xF7a\x10{V[`\x06Tt\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04`\xFF\x16\x15a\rmW`\x06\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90U`@Q\x7F\x9A_W\xDE\x85m\xD6h\xC5M\xD9^\\U\xDF\x93C!q\xCB\xCAI\xA8wmV \xEAY\xC0$P\x90`\0\x90\xA1V[`@Q\x7F\xA8c\xAE\xC9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[V[a\r\xA9a\x10{V[a\r\x9F`\0a\x17@V[a\x0E\x0F`@Q\x80a\x01\0\x01`@R\x80`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[P`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x84Qa\x01\0\x81\x01\x86R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R\x95\x90\x04\x90\x94\x16\x91\x84\x01\x91\x90\x91R`\x01\x81\x01T\x93\x83\x01\x93\x90\x93R`\x02\x83\x01T``\x83\x01R`\x03\x83\x01T`\x80\x83\x01R`\x04\x83\x01T`\xA0\x83\x01R\x82\x01T`\xC0\x82\x01R`\x06\x90\x91\x01T`\xE0\x82\x01R\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x80T`\x01`@\x1B\x81\x04`\xFF\x16\x15\x90g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\0\x81\x15\x80\x15a\x0E\xDEWP\x82[\x90P`\0\x82g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16`\x01\x14\x80\x15a\x0E\xFBWP0;\x15[\x90P\x81\x15\x80\x15a\x0F\tWP\x80\x15[\x15a\x0F@W`@Q\x7F\xF9.\xE8\xA9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\x16`\x01\x17\x85U\x83\x15a\x0F\x86W\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16`\x01`@\x1B\x17\x85U[a\x0F\x8F\x86a\x17\xC9V[a\x0F\x97a\x17\xDAV[`\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\xFF\xFF\xFF\xFF\x16`\x01`@\x1B\x17\x90Ua\x0F\xCF\x88\x88a\x17\xE2V[\x83\x15a\x10\x1AW\x84Th\xFF\0\0\0\0\0\0\0\0\x19\x16\x85U`@Q`\x01\x81R\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPPPV[a\x10,a\x10{V[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x10oW`@Q\x7F\x1EO\xBD\xF7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\0`\x04\x82\x01R`$\x01a\nvV[a\x10x\x81a\x17@V[PV[3a\x10\xAD\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\r\x9FW`@Q\x7F\x11\x8C\xDA\xA7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R3`\x04\x82\x01R`$\x01a\nvV[\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x81\x10\x80a\x0C\xBCW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1B`$\x82\x01R\x7FBn254: invalid scalar field\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[`\0\x80T`\x01`@\x1B\x80\x82\x04c\xFF\xFF\xFF\xFF\x16\x80\x84R`\x05` \x81\x81R`@\x80\x87 \x81Qa\x01\0\x81\x01\x83R\x81Tg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x82\x16\x83R\x97\x90\x04\x87\x16\x81\x85\x01R`\x01\x80\x83\x01T\x82\x85\x01R`\x02\x80\x84\x01T``\x80\x85\x01\x91\x90\x91R`\x03\x80\x86\x01T`\x80\x80\x87\x01\x82\x90R`\x04\x80\x89\x01T`\xA0\x89\x01\x81\x90R\x89\x8D\x01T`\xC0\x8A\x01\x81\x90R`\x06\x90\x9A\x01\x80T`\xE0\x90\x9A\x01\x99\x90\x99R\x8AQ\x80\x8D\x01\x94\x90\x94R\x83\x8B\x01R\x82\x85\x01\x98\x90\x98R\x88Q\x80\x83\x03\x90\x94\x01\x84R\x01\x90\x96R\x80Q\x90\x87\x01 \x85T\x83U\x94\x85\x90U\x83T\x90U\x95\x89R\x93\x90\x92R\x91T\x90U\x93\x90\x92\x90\x91`\x0C\x91a\x12T\x91\x85\x91l\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04\x16aZ\xD1V[\x82Ta\x01\0\x92\x90\x92\ng\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x81\x02\x19\x90\x93\x16\x91\x83\x16\x02\x17\x90\x91U`\0T`@Ql\x01\0\0\0\0\0\0\0\0\0\0\0\0\x90\x91\x04\x90\x91\x16\x81R\x7F\xDB5X%\x9E\x03\x9D~P\xE8\x16\xB9\xDC\xCE0\xFB\x11M\x8A\x9C\x86\xEC\xA5\xAB\x14\xB6\x01\x94\xD6\x94]?\x91P` \x01a\x08vV[`\0a\x12\xC6a\x1A\xD2V[`@\x80Q`\x08\x80\x82Ra\x01 \x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x01\0\x806\x837\x01\x90PP\x90P`\x02T\x81`\0\x81Q\x81\x10a\x13\x05Wa\x13\x05aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x83`\0\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81`\x01\x81Q\x81\x10a\x133Wa\x133aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x83` \x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81`\x02\x81Q\x81\x10a\x13aWa\x13aaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x83`@\x01Q\x81`\x03\x81Q\x81\x10a\x13\x85Wa\x13\x85aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x83``\x01Q\x81`\x04\x81Q\x81\x10a\x13\xA9Wa\x13\xA9aZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 `\x03\x01T\x82Q\x90\x91\x83\x91\x81\x10a\x13\xEDWa\x13\xEDaZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x90\x91R`@\x90 `\x04\x01T\x81Q\x82\x90`\x06\x90\x81\x10a\x141Wa\x141aZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 \x01T\x81Q\x82\x90`\x07\x90\x81\x10a\x14tWa\x14taZ\xF2V[` \x02` \x01\x01\x81\x81RPPa\x14\x8B\x82\x82\x85a \xB3V[a\x14\xC1W`@Q\x7F\t\xBD\xE39\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[PPPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14\x80a\x15`WP\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16a\x15T\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBCT`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14\x15[\x15a\r\x9FW`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x15\x9Fa\x10{V[`@Q`\x01`\x01`\xA0\x1B\x03\x82\x16\x81R\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x90` \x01a\x08vV[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x162WP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x16/\x91\x81\x01\x90a[\x08V[`\x01[a\x16sW`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16`\x04\x82\x01R`$\x01a\nvV[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x81\x14a\x16\xCFW`@Q\x7F\xAA\x1DI\xA4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x81\x01\x82\x90R`$\x01a\nvV[a\x16\xD9\x83\x83a!\x9EV[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\r\x9FW`@Q\x7F\xE0|\x8D\xBA\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x16`\x01`\x01`\xA0\x1B\x03\x84\x81\x16\x91\x82\x17\x84U`@Q\x92\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[a\x17\xD1a!\xF4V[a\x10x\x81a\"VV[a\r\x9Fa!\xF4V[\x81Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x15\x15\x80a\x18\x08WP` \x82\x01Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x15\x15[\x80a\x18\x15WP`\x80\x82\x01Q\x15[\x80a\x18\"WP`\xA0\x82\x01Q\x15[\x80a\x18/WP`\xC0\x82\x01Q\x15[\x80a\x18!\x03<6\x8A\x93b-\xD4\x05\xB9\x05\xA0\xEB4L\x98\xB9\xD7\xCF\x08\xB0\xC5\xEB\xF7\xC89`\xA0\x82\x01QR~\x13y4*Mw\xD4p\x87C\xAF\xF0\x1F\xF2z\xA1\x19\x17G\x8F\xDC\x8E+}F0\x81sWr\xEA` `\xA0\x83\x01Q\x01R\x7F\x19M\xAF\x85\xD9\xEE\xD9\x93{(\xE2\xA6\x80\xFC\xC5\xA7i\"\xC1\\\xD3\x1D\xC4\xF6\0\xE1I9\xB8 \x0C\xE7`\xC0\x82\x01QR\x7F%(\x0B\x12F$\x91\x1C\x7F\x87\xB4\xC2\xD8\x7FY\xC6\xC0~>\xEE\xB1\raM\xA2\x16\xF6!\x9F\xFEP\xB6` `\xC0\x83\x01Q\x01R\x7F\x04\x88.\xF3\x98\x99\xEA8\xC9gzH\xB8\xF8\xCCjg(N\x17\xFF\x94\x02\x89\xFA\xAA5\x9E\xEC\x9B3\xA6`\xE0\x82\x01QR\x7F\x1B\xAE\x9F6\xE6\x19\x078\xC7\x11P\x1B\xE5?)\x9B\xF6\x13H\xE6\x1E.\xF9\xD5wv\x0Ed\xF6)6\x8D` `\xE0\x83\x01Q\x01R\x7F-\x81\r0\x12\x0C\xB9>\x1A%K\x89\xED\n\xE8lv\x1FI\xB4\xF1)E\x9C\xD54\xF9U\x18Q5\x0Fa\x01\0\x82\x01QR\x7F\x0B%9M\xA5\xA1\xD45\xDA\xCC\xC2\xEA\xDD\x03\x9E,'\t\xF5\xF4/\xAB\xD9\xAF\xBA\x81^\xD6-j\xF3k` a\x01\0\x83\x01Q\x01R\x7F\x1C,\xE7\xBEW\x0B\xEA\x9EC\xF3\xD3\xD7\xCB\xCA\x84\xBD\xB4\xFC\x89\xB5:\xE6WS\x1D\xE7&p\xA6\x10^\x08a\x01 \x82\x01QR\x7F\x1A\x02U\xEC\x8C|\x87i3[\xC9\xDCk\"*\xC6\xA0Nvm\x08\xB4\\\x8C\xC5sY,\x05\xBC\x9C\x14` a\x01 \x83\x01Q\x01R\x7F\x1C\x16AY\x13k\x8F[Gs\xE13\xF4\x83\xA8\xA1\x92\xAB\x15\xD6\xD3\xEE\x01/\x17\x1B=\x02\xFDE\x06\xE7a\x01@\x82\x01QR\x7F'.\xB7\xD63\xCE\xDBh\xCE\x01\x13\xF4B\n\xB5a\x0B\x81\xB8\xBA\x1A\xB94\x8D\xB1Wa\xD4\x0E\x8D\xF5\xBA` a\x01@\x83\x01Q\x01R\x7F\x0EDf9\xAAl\xAF%\xE9>\xF7G\x08N9\xB8\xEA\x90\xAB\xF2;\xB4\x8C(\xFD_\x9B\xA7\xBAeP\"a\x01`\x82\x01QR\x7F\x03>\x19Z\x9E\xA3\xA9\xCE@\xB7+g:\xFBEDL\xA1\xB1_\x05C\xF4M\x10\xF5\xC6@\xA7\x80go` a\x01`\x83\x01Q\x01R\x7F\x0E\x8D\xB2\xB2\x89=\xF2=\xD6\x81y\x96\xF7_\x10\0\x9D\x99\"\x07\x93\xECsa\x01\xC0\x82\x01QR\x7F\x19\xEB\x12\xA7\x82|\r\xDFc\x83\xFE\x80l9S\xBD\x06\xB0\x8A\xAE{\xF2\xA0\x1FU\xC9\x86\xA8OP\xCC(` a\x01\xC0\x83\x01Q\x01R\x7F\x01V\x910\x88F\xE6\x8E\xA8V\xA2\xCB$\xC9\x90?\x0C\x86\x05\xDE\xA1\x90\x82\x91\x80\xFFk\xDD\x1Ce\x08\x03a\x01\xE0\x82\x01QR\x7F\x1F\xFDx\x9B\x15[\x8A\xCB\x13\xE0\xF6\xA4\x8BP\xF7\xAA\x80\x92T\x08\x88\xD0\t\x14\x10W\xD4V\x90\x91X$` a\x01\xE0\x83\x01Q\x01R\x7F\x05E\xACz\xA6m\xCF7\x19\x98\x848\xC8\x06\xFCbM\xE5z\xB4?\x85\x809/\x88\xC8l\x13x\xCEJa\x02\0\x82\x01QR\x7F\x16\xB7\xF2P\x84.\xCFN6\x90pj\x1E\x15-zW\xF7\x0FUo\x92\x07m\xA7\x85\xFD\xD3c\xC1\x9F\xCF` a\x02\0\x83\x01Q\x01R\x7F \xCB\x7F\xF3Z\x83\xA7\xDC1@6\xE4p\xF1L0\xFB\x0E\x98\xD3]f;$;\",\xAAo\xC7\xDBDa\x02 \x82\x01QR\x7F\x14\x9FAWDpth\xBD\xAAN\x85E \x1A\xB4\r\x191\xA7\xD3\x1F#v\x8F\xA7\xC6Ut\xEE>\xAB` a\x02 \x83\x01Q\x01R\x7F\n%\xC1\xB7W9\x06\xDCN\x19;N\xA8/\xD1\xFE|\xCE\xBCM\x92]\xAD&\xF0\xFF\t\xC8L\x9F\x1Aua\x02@\x82\x01QR\x7F\nR\x1F\xF3\x0C\x8F6fy\x8F\x84|]L7\x96X\xFB\xA1\x01V\xE7\xA9I\x9F'\x13\xFA\xE9\xBF+\xE1` a\x02@\x83\x01Q\x01R\x7F\x03\xDBe\x10\xC3\xF16)\xFD\xED\x9AZ-AeK\xBC\xE4\xEFm\x02L\xADS\x10\0Q\xD4\xA3\xF3\xEB\xC9a\x02`\x82\x01QR\x7F\x08\xE8\n\\\x8EL\x9B\x9F&\xF3\0<\xC5\x94\x03\xA1\x8D16\xAF\xD00\x86\x8D%\xCC\x8B\x80~*\xB3p` a\x02`\x83\x01Q\x01R\x90V[`\0a \xBE\x82a\"^V[a \xE1\x83`\0\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[` \x02` \x01\x01Qa\x10\xEFV[a \xF7\x83`\x01\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!\r\x83`\x02\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!#\x83`\x03\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!9\x83`\x04\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!O\x83`\x05\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!e\x83`\x06\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[a!{\x83`\x07\x81Q\x81\x10a \xD4Wa \xD4aZ\xF2V[`\0a!\x88\x85\x85\x85a#\x96V[\x90Pa!\x93\x81a%\x17V[\x91PP[\x93\x92PPPV[a!\xA7\x82a)\xDFV[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a!\xECWa\x16\xD9\x82\x82a*\x87V[a\x0C\xBCa*\xFFV[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0T`\x01`@\x1B\x90\x04`\xFF\x16a\r\x9FW`@Q\x7F\xD7\xE6\xBC\xF8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x10,a!\xF4V[\x80Qa\"i\x90a+7V[a\"v\x81` \x01Qa+7V[a\"\x83\x81`@\x01Qa+7V[a\"\x90\x81``\x01Qa+7V[a\"\x9D\x81`\x80\x01Qa+7V[a\"\xAA\x81`\xA0\x01Qa+7V[a\"\xB7\x81`\xC0\x01Qa+7V[a\"\xC4\x81`\xE0\x01Qa+7V[a\"\xD2\x81a\x01\0\x01Qa+7V[a\"\xE0\x81a\x01 \x01Qa+7V[a\"\xEE\x81a\x01@\x01Qa+7V[a\"\xFC\x81a\x01`\x01Qa+7V[a#\n\x81a\x01\x80\x01Qa+7V[a#\x18\x81a\x01\xA0\x01Qa\x10\xEFV[a#&\x81a\x01\xC0\x01Qa\x10\xEFV[a#4\x81a\x01\xE0\x01Qa\x10\xEFV[a#B\x81a\x02\0\x01Qa\x10\xEFV[a#P\x81a\x02 \x01Qa\x10\xEFV[a#^\x81a\x02@\x01Qa\x10\xEFV[a#l\x81a\x02`\x01Qa\x10\xEFV[a#z\x81a\x02\x80\x01Qa\x10\xEFV[a#\x88\x81a\x02\xA0\x01Qa\x10\xEFV[a\x10x\x81a\x02\xC0\x01Qa\x10\xEFV[a#\x9EaT\xDDV[\x83` \x01Q\x83Q\x14a#\xDCW`@Q\x7FA\xF5;\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a#\xE9\x85\x85\x85a+\xE1V[\x90P`\0a#\xFA\x86`\0\x01Qa/\x12V[\x90P`\0a$\r\x82\x84`\xA0\x01Q\x88a2\xF6V[`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x03\xC0\x806\x837PP`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a$PW\x90PP\x90P`\0a$\x89\x8A\x85\x8A\x89\x87\x87a3VV[`\xA0\x87\x01Q``\x87\x01Q\x91\x92P\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01`\0\x81\x83\x85\t`@\x80Qa\x01\0\x81\x01\x82R`\xE0\x9C\x8D\x01Q\x81R` \x81\x01\x96\x90\x96R\x85\x01RPPP``\x81\x01\x91\x90\x91R`\x80\x81\x01\x92\x90\x92R`\xA0\x82\x01Ra\x01`\x86\x01Q`\xC0\x82\x01Ra\x01\x80\x90\x95\x01Q\x92\x85\x01\x92\x90\x92RP\x91\x94\x93PPPPV[`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x80\x83\x01\x82\x90R\x83Q\x80\x85\x01\x85R\x82\x81R\x90\x81\x01\x82\x90R\x83Q`\x02\x80\x82R``\x82\x01\x90\x95R\x91\x93\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x93\x92\x85\x91\x81` \x01` \x82\x02\x806\x837PP`@\x80Q`\x02\x80\x82R``\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a%\x9DW\x90PP\x90P`\0`\x01\x90P\x80\x83`\0\x81Q\x81\x10a%\xE0Wa%\xE0aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x87`\xC0\x01Q\x82`\0\x81Q\x81\x10a&\x04Wa&\x04aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x87`\0\x01Q\x83`\x01\x81Q\x81\x10a&'Wa&'aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x87`\xE0\x01Q\x82`\x01\x81Q\x81\x10a&KWa&KaZ\xF2V[` \x02` \x01\x01\x81\x90RPa&`\x82\x84a3\x8BV[`\x80\x89\x01QQ\x90\x95P``\x93P\x83\x92P\x90P`\0a&\x7F\x82`\x02a[!V[a&\x8A\x90`\x01a[!V[\x90P\x80g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a&\xA5Wa&\xA5aU\x9EV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a&\xCEW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x80g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a&\xEAWa&\xEAaU\x9EV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a'/W\x81` \x01[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81R` \x01\x90`\x01\x90\x03\x90\x81a'\x08W\x90P[P\x92PPP`\0\x80`\0[\x89`\x80\x01QQ\x81\x10\x15a'\xD3W\x89`\x80\x01Q\x81\x81Q\x81\x10a']Wa']aZ\xF2V[` \x02` \x01\x01Q\x85\x83\x81Q\x81\x10a'wWa'waZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x89`\xA0\x01Q\x81\x81Q\x81\x10a'\x99Wa'\x99aZ\xF2V[` \x02` \x01\x01Q\x84\x83\x81Q\x81\x10a'\xB3Wa'\xB3aZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra'\xC9`\x01\x83a[!V[\x91P`\x01\x01a':V[P\x88` \x01Q\x84\x82\x81Q\x81\x10a'\xEBWa'\xEBaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88`\xC0\x01Q\x83\x82\x81Q\x81\x10a(\x0EWa(\x0EaZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra($`\x01\x82a[!V[\x89Q`@\x8B\x01Q\x91\x92P\x90`\0\x89\x82\x84\t\x90P\x80\x87\x85\x81Q\x81\x10a(JWa(JaZ\xF2V[` \x02` \x01\x01\x81\x81RPPPPP\x88`\xE0\x01Q\x83\x82\x81Q\x81\x10a(pWa(paZ\xF2V[` \x90\x81\x02\x91\x90\x91\x01\x01Ra(\x86`\x01\x82a[!V[``\x8A\x01Q\x90\x91P\x87\x81\x84\x08\x92PPa(\x9E\x82a4\x85V[\x84\x82\x81Q\x81\x10a(\xB0Wa(\xB0aZ\xF2V[` \x02` \x01\x01\x81\x81RPPa(\xE8`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x91\x82\x01R\x81Q\x80\x83\x01\x90\x92R`\x01\x82R`\x02\x90\x82\x01R\x90V[\x83\x82\x81Q\x81\x10a(\xFAWa(\xFAaZ\xF2V[` \x02` \x01\x01\x81\x90RPa)\x17a)\x12\x84\x86a3\x8BV[a4\xDBV[\x94PPPPP`\0`@Q\x80`\x80\x01`@R\x80\x7F\x01\x18\xC4\xD5\xB87\xBC\xC2\xBC\x89\xB5\xB3\x98\xB5\x97N\x9FYD\x07;2\x07\x8B~#\x1F\xEC\x93\x88\x83\xB0\x81R` \x01\x7F&\x0E\x01\xB2Q\xF6\xF1\xC7\xE7\xFFNX\x07\x91\xDE\xE8\xEAQ\xD8z5\x8E\x03\x8BN\xFE0\xFA\xC0\x93\x83\xC1\x81R` \x01\x7F\"\xFE\xBD\xA3\xC0\xC0c*VG[B\x14\xE5a^\x11\xE6\xDD?\x96\xE6\xCE\xA2\x85J\x87\xD4\xDA\xCC^U\x81R` \x01\x7F\x04\xFCci\xF7\x11\x0F\xE3\xD2QV\xC1\xBB\x9Ar\x85\x9C\xF2\xA0FA\xF9\x9B\xA4\xEEA<\x80\xDAj_\xE4\x81RP\x90Pa)\xD5\x83\x82\x84a)\xD0a5zV[a6KV[\x96\x95PPPPPPV[\x80`\x01`\x01`\xA0\x1B\x03\x16;`\0\x03a*.W`@Q\x7FL\x9C\x8C\xE3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x82\x16`\x04\x82\x01R`$\x01a\nvV[\x7F6\x08\x94\xA1;\xA1\xA3!\x06g\xC8(I-\xB9\x8D\xCA> v\xCC75\xA9 \xA3\xCAP]8+\xBC\x80T\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[```\0\x80\x84`\x01`\x01`\xA0\x1B\x03\x16\x84`@Qa*\xA4\x91\x90a[4V[`\0`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80`\0\x81\x14a*\xDFW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a*\xE4V[``\x91P[P\x91P\x91Pa*\xF4\x85\x83\x83a7/V[\x92PPP[\x92\x91PPV[4\x15a\r\x9FW`@Q\x7F\xB3\x98\x97\x9F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x80Q` \x82\x01Q`\0\x91\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\x91\x15\x90\x15\x16\x15a+qWPPPV[\x82Q` \x84\x01Q\x82`\x03\x84\x85\x85\x86\t\x85\t\x08\x83\x82\x83\t\x14\x83\x82\x10\x84\x84\x10\x16\x16\x93PPP\x81a\x16\xD9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x17`$\x82\x01R\x7FBn254: invalid G1 point\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[a,)`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@\x80Q\x80\x82\x01\x90\x91R``\x81R`\0` \x82\x01R\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01a,j\x82\x87\x87a7\xA4V[\x81Q\x84Qa,w\x90a;\x85V[a,\x84\x86` \x01Qa;\x85V[a,\x91\x87`@\x01Qa;\x85V[a,\x9E\x88``\x01Qa;\x85V[a,\xAB\x89`\x80\x01Qa;\x85V[`@Q` \x01a,\xC0\x96\x95\x94\x93\x92\x91\x90a[PV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\xDB\x82a\xF2I\x065\xEB\xA5\x0C\xB9\xC2\xE5\xE7P\x80\0\x01\x90\x82\x01R\x7F\t\xC52\xC60k\x93\xD2\x96x \rG\xC0\xB2\xA9\x9C\x18\xD5\x1B\x83\x8E\xEB\x1D>\xEDLS;\xB5\x12\xD0``\x82\x01R\x7F'$q6\x03\xBF\xBDy\n\xEA\xF3\xE7\xDF%\xD8\xE7\xEF\x8F1\x134\x90[M\x8C\x99\x98\x0C\xF2\x10\x97\x9D`\x80\x82\x01R\x90V[`@Q\x7F\xE2\xEF\t\xE5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x91\x90PV[a3\x1A`@Q\x80``\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[a3$\x84\x84a>\x08V[\x80\x82Ra34\x90\x85\x90\x85\x90a>nV[` \x82\x01R\x80Qa3J\x90\x85\x90\x84\x90\x86\x90a>\xF4V[`@\x82\x01R\x93\x92PPPV[`\0\x80a3d\x85\x87\x89a@\xBBV[\x90Pa3t\x88\x86\x89\x89\x88\x88aA\xB9V[a3\x7F\x81\x87\x86aD\xD7V[\x98\x97PPPPPPPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x82Q\x82Q\x14a3\xF0W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FMSM error: length does not match`D\x82\x01R`d\x01a\nvV[a4.\x83`\0\x81Q\x81\x10a4\x06Wa4\x06aZ\xF2V[` \x02` \x01\x01Q\x83`\0\x81Q\x81\x10a4!Wa4!aZ\xF2V[` \x02` \x01\x01QaE9V[\x90P`\x01[\x82Q\x81\x10\x15a4~Wa4t\x82a4o\x86\x84\x81Q\x81\x10a4UWa4UaZ\xF2V[` \x02` \x01\x01Q\x86\x85\x81Q\x81\x10a4!Wa4!aZ\xF2V[aE\xDDV[\x91P`\x01\x01a43V[P\x92\x91PPV[`\0a4\xB1\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x83a\\\x93V[a*\xF9\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01a\\\xB5V[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81Q` \x83\x01Q\x15\x90\x15\x16\x15a5\x03WP\x90V[`@Q\x80`@\x01`@R\x80\x83`\0\x01Q\x81R` \x01\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDG\x84` \x01Qa5H\x91\x90a\\\x93V[a5r\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDGa\\\xB5V[\x90R\x92\x91PPV[a5\xA5`@Q\x80`\x80\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@Q\x80`\x80\x01`@R\x80\x7F\x18\0\xDE\xEF\x12\x1F\x1EvBj\0f^\\DygC\"\xD4\xF7^\xDA\xDDF\xDE\xBD\\\xD9\x92\xF6\xED\x81R` \x01\x7F\x19\x8E\x93\x93\x92\rH:r`\xBF\xB71\xFB]%\xF1\xAAI35\xA9\xE7\x12\x97\xE4\x85\xB7\xAE\xF3\x12\xC2\x81R` \x01\x7F\x12\xC8^\xA5\xDB\x8Cm\xEBJ\xABq\x80\x8D\xCB@\x8F\xE3\xD1\xE7i\x0CC\xD3{L\xE6\xCC\x01f\xFA}\xAA\x81R` \x01\x7F\t\x06\x89\xD0X_\xF0u\xEC\x9E\x99\xADi\x0C3\x95\xBCK13p\xB3\x8E\xF3U\xAC\xDA\xDC\xD1\"\x97[\x81RP\x90P\x90V[`\0\x80`\0`@Q\x87Q\x81R` \x88\x01Q` \x82\x01R` \x87\x01Q`@\x82\x01R\x86Q``\x82\x01R``\x87\x01Q`\x80\x82\x01R`@\x87\x01Q`\xA0\x82\x01R\x85Q`\xC0\x82\x01R` \x86\x01Q`\xE0\x82\x01R` \x85\x01Qa\x01\0\x82\x01R\x84Qa\x01 \x82\x01R``\x85\x01Qa\x01@\x82\x01R`@\x85\x01Qa\x01`\x82\x01R` `\0a\x01\x80\x83`\x08Z\xFA\x91PP`\0Q\x91P\x80a7!W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1C`$\x82\x01R\x7FBn254: Pairing check failed!\0\0\0\0`D\x82\x01R`d\x01a\nvV[P\x15\x15\x90P[\x94\x93PPPPV[``\x82a7DWa7?\x82aF\x84V[a!\x97V[\x81Q\x15\x80\x15a7[WP`\x01`\x01`\xA0\x1B\x03\x84\x16;\x15[\x15a7\x9DW`@Q\x7F\x99\x96\xB3\x15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x01`\x01`\xA0\x1B\x03\x85\x16`\x04\x82\x01R`$\x01a\nvV[P\x80a!\x97V[\x82Q`\xFE\x90a7\xDFa7\xB5\x83a<\xBCV[`@Q` \x01a7\xC7\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x04aF\xC6V[a8\x19a7\xEF\x86`\0\x01Qa<\xBCV[`@Q` \x01a8\x01\x91\x81R` \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R`\0`\x08aF\xC6V[a8)a7\xEF\x87` \x01Qa<\xBCV[`@Q` \x01a8<\x94\x93\x92\x91\x90a\\\xC8V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x85Ra8Y`\x01a<\xBCV[a8\x82\x7F/\x8D\xD1\xF1\xA7X^W\x84\x93P`\0[\x82\x81\x10\x15a>RW\x83\x85\x86\t\x94P`\x01\x01a>eV[`\x01\x83\x03\x93P[PPP\x92\x91PPV[`\0\x82`\x01\x03a>\x80WP`\x01a!\x97V[\x81`\0\x03a>\x90WP`\0a!\x97V[`@\x84\x01Q\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90`\0\x90\x82\x81\x86\t\x90P\x85\x80\x15a>\xD2W`\x01\x87\x03\x92Pa>\xD9V[`\x01\x84\x03\x92P[Pa>\xE3\x82aG\xEEV[\x91P\x82\x82\x82\t\x97\x96PPPPPPPV[\x82Q`\0\x90\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90\x83\x83\x03a?\x87W`\x01`\0[\x82\x81\x10\x15a?zW\x81\x87\x03a?[W\x87\x81\x81Q\x81\x10a?HWa?HaZ\xF2V[` \x02` \x01\x01Q\x94PPPPPa7'V[\x83\x80a?iWa?ia\\}V[\x89``\x01Q\x83\t\x91P`\x01\x01a?'V[P`\0\x93PPPPa7'V[`\0\x80`\0\x80\x8A`@\x01Q\x90P`\0\x80a?\xA1\x8D\x88aH\xA6V[\x90P`\0\x87g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a?\xBEWa?\xBEaU\x9EV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a?\xE7W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P\x88\x8B\x85\t\x93P`\x01\x92P`\0[\x88\x81\x10\x15a@,W` \x81\x02` \x84\x01\x01Q\x95P\x89\x8D\x87\x8C\x03\x08\x96P\x89\x87\x85\t` \x82\x81\x02\x84\x01\x01\x88\x90R\x93P`\x01\x01a?\xF7V[Pa@6\x83aG\xEEV[\x92P`\0[\x88\x81\x10\x15a@\xA9W` \x81\x02` \x84\x01\x01Q\x95P\x89\x86\x86\t\x97P\x89\x84\x89\t\x97P`\0[\x89\x81\x10\x15a@\x88W\x80\x82\x14a@\x80W` \x81\x02` \x84\x01\x01Q\x97P\x8A\x88\x8A\t\x98P[`\x01\x01a@^V[P` \x81\x02` \x8F\x01\x01Q\x95P\x89\x86\x89\t\x97P\x89\x88\x8C\x08\x9AP`\x01\x01a@;V[PPPPPPPPPP\x94\x93PPPPV[`\0\x80\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P`\0\x83` \x01Q\x90P`\0\x84`@\x01Q\x90P`\0`\x01\x90P``\x88\x01Q`\x80\x89\x01Qa\x01\xA0\x89\x01Qa\x02@\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xC0\x89\x01Qa\x02`\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xE0\x89\x01Qa\x02\x80\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02\0\x89\x01Qa\x02\xA0\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02 \x89\x01Q\x91Pa\x02\xC0\x89\x01Q\x86\x87\x82\x89\x85\x87\x08\t\x85\t\x93PPPP\x87Q` \x89\x01Q\x85\x86\x86\x83\t\x87\x03\x85\x08\x96PP\x84\x85\x83\x83\t\x86\x03\x87\x08\x99\x98PPPPPPPPPV[aA\xC7\x86\x86\x86\x86\x85\x87aI\x97V[`\xC0\x85\x01Q\x82Q\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x91\x90\x81\x90\x81\x90\x86\x90`\x14\x90\x81\x10aB\x08WaB\x08aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85`\0\x01Q\x84`\x14\x81Q\x81\x10aB,WaB,aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x15\x81Q\x81\x10aBQWaBQaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85` \x01Q\x84`\x15\x81Q\x81\x10aBuWaBuaZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x16\x81Q\x81\x10aB\x9AWaB\x9AaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85`@\x01Q\x84`\x16\x81Q\x81\x10aB\xBEWaB\xBEaZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x17\x81Q\x81\x10aB\xE3WaB\xE3aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85``\x01Q\x84`\x17\x81Q\x81\x10aC\x07WaC\x07aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x18\x81Q\x81\x10aC,WaC,aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85`\x80\x01Q\x84`\x18\x81Q\x81\x10aCPWaCPaZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x19\x81Q\x81\x10aCuWaCuaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88`@\x01Q\x84`\x19\x81Q\x81\x10aC\x99WaC\x99aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1A\x81Q\x81\x10aC\xBEWaC\xBEaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88``\x01Q\x84`\x1A\x81Q\x81\x10aC\xE2WaC\xE2aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1B\x81Q\x81\x10aD\x07WaD\x07aZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88`\x80\x01Q\x84`\x1B\x81Q\x81\x10aD+WaD+aZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1C\x81Q\x81\x10aDPWaDPaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x88`\xA0\x01Q\x84`\x1C\x81Q\x81\x10aDtWaDtaZ\xF2V[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x87`\xE0\x01Q\x85`\x1D\x81Q\x81\x10aD\x9DWaD\x9DaZ\xF2V[` \x02` \x01\x01\x81\x81RPP\x85`\xA0\x01Q\x84`\x1D\x81Q\x81\x10aD\xC1WaD\xC1aZ\xF2V[` \x02` \x01\x01\x81\x90RPPPPPPPPPPV[\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x83\x81\x03\x90`\0[`\n\x81\x10\x15aE0W` `\x15\x82\x01\x02\x84\x01Q` \x82\x02a\x01\xA0\x01\x86\x01Q\x83\x84\x82\x84\t\x86\x08\x94PPP`\x01\x01aD\xFFV[PP\x93\x92PPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RaEUaU0V[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x81\x01\x83\x90R`\0``\x83`\x80\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80aE\x87W`\0\x80\xFD[P\x80aE\xD5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01R\x7FBn254: scalar mul failed!\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[PP\x92\x91PPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RaE\xF9aUNV[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x83\x01R\x83\x01Q``\x80\x83\x01\x91\x90\x91R`\0\x90\x83`\xC0\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80aF6W`\0\x80\xFD[P\x80aE\xD5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: group addition failed!\0\0\0`D\x82\x01R`d\x01a\nvV[\x80Q\x15aF\x94W\x80Q\x80\x82` \x01\xFD[`@Q\x7F\x14%\xEAB\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x81aF\xD4\x81`\x1Fa[!V[\x10\x15aG\"W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x0E`$\x82\x01R\x7Fslice_overflow\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[aG,\x82\x84a[!V[\x84Q\x10\x15aG|W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x11`$\x82\x01R\x7Fslice_outOfBounds\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\nvV[``\x82\x15\x80\x15aG\x9BW`@Q\x91P`\0\x82R` \x82\x01`@RaG\xE5V[`@Q\x91P`\x1F\x84\x16\x80\x15` \x02\x81\x84\x01\x01\x85\x81\x01\x87\x83\x15` \x02\x84\x8B\x01\x01\x01[\x81\x83\x10\x15aG\xD4W\x80Q\x83R` \x92\x83\x01\x92\x01aG\xBCV[PP\x85\x84R`\x1F\x01`\x1F\x19\x16`@RP[P\x94\x93PPPPV[`\0\x80`\0\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P`@Q` \x81R` \x80\x82\x01R` `@\x82\x01R\x84``\x82\x01R`\x02\x82\x03`\x80\x82\x01R\x81`\xA0\x82\x01R` `\0`\xC0\x83`\x05Z\xFA\x92PP`\0Q\x92P\x81aH\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: pow precompile failed!\0\0\0`D\x82\x01R`d\x01a\nvV[PP\x91\x90PV[``\x82` \x01Q\x82\x11\x15aH\xE6W`@Q\x7F\x8C^\x11\xF1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x83\x01Q`\x01\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x84g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15aI'WaI'aU\x9EV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15aIPW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x84\x15\x19\x15a>eW` \x84\x01\x85` \x02\x81\x01`\x01\x82R` \x82\x01\x91P[\x80\x82\x10\x15aI\x8CW\x82\x85\x85\t\x93P\x83\x82R` \x82\x01\x91PaIpV[PPPPP\x92\x91PPV[`\0\x80`\0\x80`\0\x80\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x90P\x80` \x8B\x01Q` \x8D\x01Q\t\x95P\x8AQ\x93P\x80`\xA0\x8C\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80\x7F/\x8D\xD1\xF1\xA7X\x82a\x16\xC3V[a\x08l\x82\x82a\x16\xFAV[`\0a\x0CRa\x17\xB3V[P`\0\x80Q` a]\x9C\x839\x81Q\x91R\x90V[a\x0Cq`\x08`\0aPQV[`\0[\x81Q\x81\x10\x15a\x08lW`\x08\x82\x82\x81Q\x81\x10a\x0C\x91Wa\x0C\x91aYQV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01Q\x82T`\x01\x80\x82\x01\x85U`\0\x94\x85R\x93\x83\x90 \x82Q`\x02\x90\x92\x02\x01\x80T`\x01`\x01`@\x1B\x03\x19\x16`\x01`\x01`@\x1B\x03\x90\x92\x16\x91\x90\x91\x17\x81U\x91\x01Q\x90\x82\x01U\x01a\x0CtV[a\x0C\xE9a\x12UV[`\x06T`\x01`\xA0\x1B\x90\x04`\xFF\x16\x15a\r6W`\x06\x80T`\x01`\x01`\xA8\x1B\x03\x19\x16\x90U`@Q\x7F\x9A_W\xDE\x85m\xD6h\xC5M\xD9^\\U\xDF\x93C!q\xCB\xCAI\xA8wmV \xEAY\xC0$P\x90`\0\x90\xA1V[`@Qc\xA8c\xAE\xC9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[V[a\rYa\x12UV[a\rO`\0a\x17\xFCV[a\rkaO\xFAV[P`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x93\x84\x90 \x84Qa\x01\0\x81\x01\x86R\x81T`\x01`\x01`@\x1B\x03\x80\x82\x16\x83R\x95\x90\x04\x90\x94\x16\x91\x84\x01\x91\x90\x91R`\x01\x81\x01T\x93\x83\x01\x93\x90\x93R`\x02\x83\x01T``\x83\x01R`\x03\x83\x01T`\x80\x83\x01R`\x04\x83\x01T`\xA0\x83\x01R\x82\x01T`\xC0\x82\x01R`\x06\x90\x91\x01T`\xE0\x82\x01R\x90V[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R`\x08\x80T\x90a\x0E\x17`\x01\x83aYgV[\x81T\x81\x10a\x0E'Wa\x0E'aYQV[`\0\x91\x82R` \x90\x91 `\x02\x90\x91\x02\x01T`\x01`\x01`@\x1B\x03\x16\x83\x10a\x0E`W`@Qc\x18V\xA4\x99`\xE2\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0[\x81\x81\x10\x15a\x0E\xF5W\x83`\x08\x82\x81T\x81\x10a\x0E\x7FWa\x0E\x7FaYQV[`\0\x91\x82R` \x90\x91 `\x02\x90\x91\x02\x01T`\x01`\x01`@\x1B\x03\x16\x11\x15a\x0E\xEDW`\x08\x81\x81T\x81\x10a\x0E\xB2Wa\x0E\xB2aYQV[`\0\x91\x82R` \x91\x82\x90 `@\x80Q\x80\x82\x01\x90\x91R`\x02\x90\x92\x02\x01\x80T`\x01`\x01`@\x1B\x03\x16\x82R`\x01\x01T\x91\x81\x01\x91\x90\x91R\x94\x93PPPPV[`\x01\x01a\x0EcV[P`\x08a\x0F\x03`\x01\x83aYgV[\x81T\x81\x10a\x0F\x13Wa\x0F\x13aYQV[`\0\x91\x82R` \x91\x82\x90 `@\x80Q\x80\x82\x01\x90\x91R`\x02\x90\x92\x02\x01\x80T`\x01`\x01`@\x1B\x03\x16\x82R`\x01\x01T\x91\x81\x01\x91\x90\x91R\x93\x92PPPV[`\0\x80a\x0FXa\x18XV[T`\x01`\x01`\xA0\x1B\x03\x16\x92\x91PPV[`\0a\x0Fra\x18|V[\x80T\x90\x91P`\xFF`\x01`@\x1B\x82\x04\x16\x15\x90`\x01`\x01`@\x1B\x03\x16`\0\x81\x15\x80\x15a\x0F\x99WP\x82[\x90P`\0\x82`\x01`\x01`@\x1B\x03\x16`\x01\x14\x80\x15a\x0F\xB5WP0;\x15[\x90P\x81\x15\x80\x15a\x0F\xC3WP\x80\x15[\x15a\x0F\xE1W`@Qc\xF9.\xE8\xA9`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x84T`\x01`\x01`@\x1B\x03\x19\x16`\x01\x17\x85U\x83\x15a\x10\nW\x84T`\xFF`@\x1B\x19\x16`\x01`@\x1B\x17\x85U[a\x10\x13\x86a\x18\xA0V[a\x10\x1Ba\x18\xB1V[`\0\x80T`\x01` \x1B`\x01``\x1B\x03\x19\x16`\x01`@\x1B\x17\x90Ua\x10>\x88\x88a\x18\xB9V[\x83\x15a\x10\x89W\x84T`\xFF`@\x1B\x19\x16\x85U`@Q\x7F\xC7\xF5\x05\xB2\xF3q\xAE!u\xEEI\x13\xF4I\x9E\x1F&3\xA7\xB5\x93c!\xEE\xD1\xCD\xAE\xB6\x11Q\x81\xD2\x90a\x10\x80\x90`\x01\x90aW\xFCV[`@Q\x80\x91\x03\x90\xA1[PPPPPPPPV[`\x07\x81\x81T\x81\x10a\x10\xA3W`\0\x80\xFD[`\0\x91\x82R` \x90\x91 \x01T\x90P\x81V[`\x80\x80\x82\x01Q`\xA0\x83\x01Q`\xC0\x84\x01Q`@\x80Q` \x81\x01\x94\x90\x94R\x83\x01\x91\x90\x91R``\x82\x01R`\0\x91\x01`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[`\x08\x81\x81T\x81\x10a\x11\x0CW`\0\x80\xFD[`\0\x91\x82R` \x90\x91 `\x02\x90\x91\x02\x01\x80T`\x01\x90\x91\x01T`\x01`\x01`@\x1B\x03\x90\x91\x16\x91P\x82V[`\x07T`\0\x90C\x84\x11\x80a\x11HWP`\x03\x81\x10[\x15a\x11fW`@Qc\xB0\xB48w`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0\x80\x80a\x11u`\x01\x85aYgV[\x90P[\x81a\x11\xE0W\x86`\x07\x82\x81T\x81\x10a\x11\x91Wa\x11\x91aYQV[\x90`\0R` `\0 \x01T\x11a\x11\xC6W`\x01\x91P`\x07\x81\x81T\x81\x10a\x11\xB8Wa\x11\xB8aYQV[\x90`\0R` `\0 \x01T\x92P[`\x02\x81\x10a\x11\xE0W\x80a\x11\xD8\x81aYzV[\x91PPa\x11xV[\x81a\x11\xFEW`@Qc\xB0\xB48w`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x85a\x12\t\x84\x89aYgV[\x11\x94PPPPP[\x92\x91PPV[a\x12\x1Fa\x12UV[`\x01`\x01`\xA0\x1B\x03\x81\x16a\x12IW`\0`@Qc\x1EO\xBD\xF7`\xE0\x1B\x81R`\x04\x01a\t\xDA\x91\x90aS\xD5V[a\x12R\x81a\x17\xFCV[PV[3a\x12^a\x0FMV[`\x01`\x01`\xA0\x1B\x03\x16\x14a\rOW3`@Qc\x11\x8C\xDA\xA7`\xE0\x1B\x81R`\x04\x01a\t\xDA\x91\x90aS\xD5V[`\0\x80Q` a]\xBC\x839\x81Q\x91R\x81\x10\x80a\x08lW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1B`$\x82\x01Rz\x10\x9B\x8C\x8DM\x0E\x88\x1A[\x9D\x98[\x1AY\x08\x1C\xD8\xD8[\x18\\\x88\x19\x9AY[\x19`*\x1B`D\x82\x01R`d\x01a\t\xDAV[`\0\x80Tc\xFF\xFF\xFF\xFF`\x01`@\x1B\x91\x82\x90\x04\x16\x82R`\x05` \x81\x81R`@\x80\x85 \x81Qa\x01\0\x81\x01\x83R\x81T`\x01`\x01`@\x1B\x03\x80\x82\x16\x83R\x96\x90\x04\x90\x95\x16\x92\x85\x01\x92\x90\x92R`\x01\x82\x01T\x90\x84\x01R`\x02\x81\x01T``\x84\x01R`\x03\x81\x01T`\x80\x84\x01R`\x04\x81\x01T`\xA0\x84\x01R\x90\x81\x01T`\xC0\x83\x01R`\x06\x01T`\xE0\x82\x01Ra\x13k\x90a\x10\xB4V[`\x03\x80T`\x01\x90\x81U\x90\x82\x90U`\x04\x80T`\x02U`\0\x80T`\x01`@\x1B\x81\x04c\xFF\xFF\xFF\xFF\x16\x82R`\x05` R`@\x82 `\x06\x01T\x90\x92U\x92\x93P\x90\x91\x90`\x0C\x90a\x13\xC6\x90\x84\x90`\x01``\x1B\x90\x04`\x01`\x01`@\x1B\x03\x16aY\x91V[\x92Pa\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP\x7F\xDB5X%\x9E\x03\x9D~P\xE8\x16\xB9\xDC\xCE0\xFB\x11M\x8A\x9C\x86\xEC\xA5\xAB\x14\xB6\x01\x94\xD6\x94]?`\0`\x0C\x90T\x90a\x01\0\n\x90\x04`\x01`\x01`@\x1B\x03\x16`@Qa\x08B\x91\x90aW\xFCV[`\0a\x148a\x1B\xDCV[`@\x80Q`\x08\x80\x82Ra\x01 \x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x01\0\x806\x837\x01\x90PP\x90P`\x02T\x81`\0\x81Q\x81\x10a\x14wWa\x14waYQV[` \x02` \x01\x01\x81\x81RPP\x83`\0\x01Q`\x01`\x01`@\x1B\x03\x16\x81`\x01\x81Q\x81\x10a\x14\xA4Wa\x14\xA4aYQV[` \x02` \x01\x01\x81\x81RPP\x83` \x01Q`\x01`\x01`@\x1B\x03\x16\x81`\x02\x81Q\x81\x10a\x14\xD1Wa\x14\xD1aYQV[` \x02` \x01\x01\x81\x81RPP\x83`@\x01Q\x81`\x03\x81Q\x81\x10a\x14\xF5Wa\x14\xF5aYQV[` \x02` \x01\x01\x81\x81RPP\x83``\x01Q\x81`\x04\x81Q\x81\x10a\x15\x19Wa\x15\x19aYQV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 `\x03\x01T\x82Q\x90\x91\x83\x91\x81\x10a\x15]Wa\x15]aYQV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x90\x91R`@\x90 `\x04\x01T\x81Q\x82\x90`\x06\x90\x81\x10a\x15\xA1Wa\x15\xA1aYQV[` \x90\x81\x02\x91\x90\x91\x01\x81\x01\x91\x90\x91R`\0\x80T`\x01`@\x1B\x90\x04c\xFF\xFF\xFF\xFF\x16\x81R`\x05\x91\x82\x90R`@\x90 \x01T\x81Q\x82\x90`\x07\x90\x81\x10a\x15\xE4Wa\x15\xE4aYQV[` \x02` \x01\x01\x81\x81RPPa\x15\xFB\x82\x82\x85a!\xBDV[a\x16\x18W`@Qc\t\xBD\xE39`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[PPPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14\x80a\x16\xA5WP\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16a\x16\x99`\0\x80Q` a]\x9C\x839\x81Q\x91RT`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x14\x15[\x15a\rOW`@Qcp>F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x16\xCBa\x12UV[\x7F\xF7\x87!\"n\xFE\x9A\x1B\xB6x\x18\x9A\x16\xD1UI(\xB9\xF2\x19.,\xB9>\xED\xA8;y\xFA@\0}\x81`@Qa\x08B\x91\x90aS\xD5V[\x81`\x01`\x01`\xA0\x1B\x03\x16cR\xD1\x90-`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x92PPP\x80\x15a\x17TWP`@\x80Q`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01\x90\x92Ra\x17Q\x91\x81\x01\x90aY\xB1V[`\x01[a\x17sW\x81`@QcL\x9C\x8C\xE3`\xE0\x1B\x81R`\x04\x01a\t\xDA\x91\x90aS\xD5V[`\0\x80Q` a]\x9C\x839\x81Q\x91R\x81\x14a\x17\xA4W`@Qc*\x87Ri`\xE2\x1B\x81R`\x04\x81\x01\x82\x90R`$\x01a\t\xDAV[a\x17\xAE\x83\x83a\"\xA8V[PPPV[0`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a\rOW`@Qcp>F\xDD`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a\x18\x06a\x18XV[\x80T`\x01`\x01`\xA0\x1B\x03\x84\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x93\x94P\x91\x16\x91\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPPV[\x7F\x90\x16\xD0\x9Dr\xD4\x0F\xDA\xE2\xFD\x8C\xEA\xC6\xB6#Lw\x06!O\xD3\x9C\x1C\xD1\xE6\t\xA0R\x8C\x19\x93\0\x90V[\x7F\xF0\xC5~\x16\x84\r\xF0@\xF1P\x88\xDC/\x81\xFE9\x1C9#\xBE\xC7>#\xA9f.\xFC\x9C\"\x9Cj\0\x90V[a\x18\xA8a\"\xFEV[a\x12R\x81a##V[a\rOa\"\xFEV[\x81Q`\x01`\x01`@\x1B\x03\x16\x15\x15\x80a\x18\xDDWP` \x82\x01Q`\x01`\x01`@\x1B\x03\x16\x15\x15[\x80a\x18\xEAWP`\x80\x82\x01Q\x15[\x80a\x18\xF7WP`\xA0\x82\x01Q\x15[\x80a\x19\x04WP`\xC0\x82\x01Q\x15[\x80a\x19\x11WP`\xE0\x82\x01Q\x15[\x80a\x19 WPc\xFF\xFF\xFF\xFF\x81\x16\x15[\x15a\x19>W`@QcP\xDD\x03\xF7`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x81`\x05`\0\x80`\x04\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP\x81`\x05`\0\x80`\x08\x90T\x90a\x01\0\n\x90\x04c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16c\xFF\xFF\xFF\xFF\x16\x81R` \x01\x90\x81R` \x01`\0 `\0\x82\x01Q\x81`\0\x01`\0a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP` \x82\x01Q\x81`\0\x01`\x08a\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP`@\x82\x01Q\x81`\x01\x01U``\x82\x01Q\x81`\x02\x01U`\x80\x82\x01Q\x81`\x03\x01U`\xA0\x82\x01Q\x81`\x04\x01U`\xC0\x82\x01Q\x81`\x05\x01U`\xE0\x82\x01Q\x81`\x06\x01U\x90PP`\0\x80`\x0Ca\x01\0\n\x81T\x81`\x01`\x01`@\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`@\x1B\x03\x16\x02\x17\x90UP\x80`\0\x80a\x01\0\n\x81T\x81c\xFF\xFF\xFF\xFF\x02\x19\x16\x90\x83c\xFF\xFF\xFF\xFF\x16\x02\x17\x90UP`\0a\x1B$\x83a\x10\xB4V[`\x01\x81\x81U`\xE0\x85\x01Q`\x02\x81\x81U`\x03\x93\x90\x93U`\x04U`\x07\x80T\x80\x83\x01\x82U`\0\x91\x82RC`\0\x80Q` a]\\\x839\x81Q\x91R\x90\x91\x01U`@\x80Q\x80\x82\x01\x82R` \x80\x89\x01Q`\x01`\x01`@\x1B\x03\x90\x81\x16\x83R\x92\x90\x98\x01Q\x97\x81\x01\x97\x88R`\x08\x80T\x94\x85\x01\x81U\x90\x92R\x90Q`\0\x80Q` a]\xDC\x839\x81Q\x91R\x92\x90\x93\x02\x91\x82\x01\x80T`\x01`\x01`@\x1B\x03\x19\x16\x93\x90\x91\x16\x92\x90\x92\x17\x90\x91U\x92Q`\0\x80Q` a^\x1C\x839\x81Q\x91R\x90\x93\x01\x92\x90\x92UPPV[a\x1B\xE4aPrV[b\x01\0\0\x81R`\x08` \x82\x01R\x7F\x01=\x1DKBQy%\x8BWx`9yU\xCB\xFA\x08\x16\xE3+\x1C%\xA1\xFDsL\x91\xB9Q\xEE\x81`@\x82\x01QR\x7F\x16\xB8\x8D\xC7C\x9Am\x84\x1E\x1A\x11\x03\xF5\xA3\xD2\xD2D\x017\xF1\x8D\x02v5\x03\xBA\xC7\xB4]\xCB\x98;` `@\x83\x01Q\x01R\x7F\x0C<\x86O\x19_Y\x11\x99'\xF58W\xF1\xDE\x8B\xF5u\x94\x17H\xB755\x1F\xD3\x13s\xC7\x87\\-``\x82\x01QR\x7F\x16\x9B\xA1Q\x07\xF2\xEF\xF9\xB94\x1B\xF3\x07B\xA88\xD2}\xBDi\xE8\x8B#S\xDC\xA8Y/\x15\xF1\x11\x1C` ``\x83\x01Q\x01R\x7F\x11\xD4\xCE\xB1Ya\xD1\x0BaV\xAE=\t\xBBx\xB4\xDFE\xFB\x85C\x06\x08\x84\xE7\xD4\0u[\xEBJ\xC8`\x80\x82\x01QR\x7F\x03&\xFF\x069\x1E\xD5\xD2n\xC1\xBC\x08\x0B\x8DF\x01N\xE2,\x0Ch\xED\x02/\x16 \xC4\xD9\xD3\x847\xD3` `\x80\x83\x01Q\x01R\x7F#a\x0C\xB4>!\x03<6\x8A\x93b-\xD4\x05\xB9\x05\xA0\xEB4L\x98\xB9\xD7\xCF\x08\xB0\xC5\xEB\xF7\xC89`\xA0\x82\x01QR~\x13y4*Mw\xD4p\x87C\xAF\xF0\x1F\xF2z\xA1\x19\x17G\x8F\xDC\x8E+}F0\x81sWr\xEA` `\xA0\x83\x01Q\x01R\x7F\x19M\xAF\x85\xD9\xEE\xD9\x93{(\xE2\xA6\x80\xFC\xC5\xA7i\"\xC1\\\xD3\x1D\xC4\xF6\0\xE1I9\xB8 \x0C\xE7`\xC0\x82\x01QR\x7F%(\x0B\x12F$\x91\x1C\x7F\x87\xB4\xC2\xD8\x7FY\xC6\xC0~>\xEE\xB1\raM\xA2\x16\xF6!\x9F\xFEP\xB6` `\xC0\x83\x01Q\x01R\x7F\x04\x88.\xF3\x98\x99\xEA8\xC9gzH\xB8\xF8\xCCjg(N\x17\xFF\x94\x02\x89\xFA\xAA5\x9E\xEC\x9B3\xA6`\xE0\x82\x01QR\x7F\x1B\xAE\x9F6\xE6\x19\x078\xC7\x11P\x1B\xE5?)\x9B\xF6\x13H\xE6\x1E.\xF9\xD5wv\x0Ed\xF6)6\x8D` `\xE0\x83\x01Q\x01R\x7F-\x81\r0\x12\x0C\xB9>\x1A%K\x89\xED\n\xE8lv\x1FI\xB4\xF1)E\x9C\xD54\xF9U\x18Q5\x0Fa\x01\0\x82\x01QR\x7F\x0B%9M\xA5\xA1\xD45\xDA\xCC\xC2\xEA\xDD\x03\x9E,'\t\xF5\xF4/\xAB\xD9\xAF\xBA\x81^\xD6-j\xF3k` a\x01\0\x83\x01Q\x01R\x7F\x1C,\xE7\xBEW\x0B\xEA\x9EC\xF3\xD3\xD7\xCB\xCA\x84\xBD\xB4\xFC\x89\xB5:\xE6WS\x1D\xE7&p\xA6\x10^\x08a\x01 \x82\x01QR\x7F\x1A\x02U\xEC\x8C|\x87i3[\xC9\xDCk\"*\xC6\xA0Nvm\x08\xB4\\\x8C\xC5sY,\x05\xBC\x9C\x14` a\x01 \x83\x01Q\x01R\x7F\x1C\x16AY\x13k\x8F[Gs\xE13\xF4\x83\xA8\xA1\x92\xAB\x15\xD6\xD3\xEE\x01/\x17\x1B=\x02\xFDE\x06\xE7a\x01@\x82\x01QR\x7F'.\xB7\xD63\xCE\xDBh\xCE\x01\x13\xF4B\n\xB5a\x0B\x81\xB8\xBA\x1A\xB94\x8D\xB1Wa\xD4\x0E\x8D\xF5\xBA` a\x01@\x83\x01Q\x01R\x7F\x0EDf9\xAAl\xAF%\xE9>\xF7G\x08N9\xB8\xEA\x90\xAB\xF2;\xB4\x8C(\xFD_\x9B\xA7\xBAeP\"a\x01`\x82\x01QR\x7F\x03>\x19Z\x9E\xA3\xA9\xCE@\xB7+g:\xFBEDL\xA1\xB1_\x05C\xF4M\x10\xF5\xC6@\xA7\x80go` a\x01`\x83\x01Q\x01R\x7F\x0E\x8D\xB2\xB2\x89=\xF2=\xD6\x81y\x96\xF7_\x10\0\x9D\x99\"\x07\x93\xECsa\x01\xC0\x82\x01QR\x7F\x19\xEB\x12\xA7\x82|\r\xDFc\x83\xFE\x80l9S\xBD\x06\xB0\x8A\xAE{\xF2\xA0\x1FU\xC9\x86\xA8OP\xCC(` a\x01\xC0\x83\x01Q\x01R\x7F\x01V\x910\x88F\xE6\x8E\xA8V\xA2\xCB$\xC9\x90?\x0C\x86\x05\xDE\xA1\x90\x82\x91\x80\xFFk\xDD\x1Ce\x08\x03a\x01\xE0\x82\x01QR\x7F\x1F\xFDx\x9B\x15[\x8A\xCB\x13\xE0\xF6\xA4\x8BP\xF7\xAA\x80\x92T\x08\x88\xD0\t\x14\x10W\xD4V\x90\x91X$` a\x01\xE0\x83\x01Q\x01R\x7F\x05E\xACz\xA6m\xCF7\x19\x98\x848\xC8\x06\xFCbM\xE5z\xB4?\x85\x809/\x88\xC8l\x13x\xCEJa\x02\0\x82\x01QR\x7F\x16\xB7\xF2P\x84.\xCFN6\x90pj\x1E\x15-zW\xF7\x0FUo\x92\x07m\xA7\x85\xFD\xD3c\xC1\x9F\xCF` a\x02\0\x83\x01Q\x01R\x7F \xCB\x7F\xF3Z\x83\xA7\xDC1@6\xE4p\xF1L0\xFB\x0E\x98\xD3]f;$;\",\xAAo\xC7\xDBDa\x02 \x82\x01QR\x7F\x14\x9FAWDpth\xBD\xAAN\x85E \x1A\xB4\r\x191\xA7\xD3\x1F#v\x8F\xA7\xC6Ut\xEE>\xAB` a\x02 \x83\x01Q\x01R\x7F\n%\xC1\xB7W9\x06\xDCN\x19;N\xA8/\xD1\xFE|\xCE\xBCM\x92]\xAD&\xF0\xFF\t\xC8L\x9F\x1Aua\x02@\x82\x01QR\x7F\nR\x1F\xF3\x0C\x8F6fy\x8F\x84|]L7\x96X\xFB\xA1\x01V\xE7\xA9I\x9F'\x13\xFA\xE9\xBF+\xE1` a\x02@\x83\x01Q\x01R\x7F\x03\xDBe\x10\xC3\xF16)\xFD\xED\x9AZ-AeK\xBC\xE4\xEFm\x02L\xADS\x10\0Q\xD4\xA3\xF3\xEB\xC9a\x02`\x82\x01QR\x7F\x08\xE8\n\\\x8EL\x9B\x9F&\xF3\0<\xC5\x94\x03\xA1\x8D16\xAF\xD00\x86\x8D%\xCC\x8B\x80~*\xB3p` a\x02`\x83\x01Q\x01R\x90V[`\0a!\xC8\x82a#+V[a!\xEB\x83`\0\x81Q\x81\x10a!\xDEWa!\xDEaYQV[` \x02` \x01\x01Qa\x12\x87V[a\"\x01\x83`\x01\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"\x17\x83`\x02\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"-\x83`\x03\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"C\x83`\x04\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"Y\x83`\x05\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"o\x83`\x06\x81Q\x81\x10a!\xDEWa!\xDEaYQV[a\"\x85\x83`\x07\x81Q\x81\x10a!\xDEWa!\xDEaYQV[`\0a\"\x92\x85\x85\x85a$cV[\x90Pa\"\x9D\x81a%\xADV[\x91PP[\x93\x92PPPV[a\"\xB1\x82a*\x0CV[`@Q`\x01`\x01`\xA0\x1B\x03\x83\x16\x90\x7F\xBC|\xD7Z \xEE'\xFD\x9A\xDE\xBA\xB3 A\xF7U!M\xBCk\xFF\xA9\x0C\xC0\"[9\xDA.\\-;\x90`\0\x90\xA2\x80Q\x15a\"\xF6Wa\x17\xAE\x82\x82a*hV[a\x08la*\xDEV[a#\x06a*\xFDV[a\rOW`@Qc\x1A\xFC\xD7\x9F`\xE3\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[a\x12\x1Fa\"\xFEV[\x80Qa#6\x90a+\x17V[a#C\x81` \x01Qa+\x17V[a#P\x81`@\x01Qa+\x17V[a#]\x81``\x01Qa+\x17V[a#j\x81`\x80\x01Qa+\x17V[a#w\x81`\xA0\x01Qa+\x17V[a#\x84\x81`\xC0\x01Qa+\x17V[a#\x91\x81`\xE0\x01Qa+\x17V[a#\x9F\x81a\x01\0\x01Qa+\x17V[a#\xAD\x81a\x01 \x01Qa+\x17V[a#\xBB\x81a\x01@\x01Qa+\x17V[a#\xC9\x81a\x01`\x01Qa+\x17V[a#\xD7\x81a\x01\x80\x01Qa+\x17V[a#\xE5\x81a\x01\xA0\x01Qa\x12\x87V[a#\xF3\x81a\x01\xC0\x01Qa\x12\x87V[a$\x01\x81a\x01\xE0\x01Qa\x12\x87V[a$\x0F\x81a\x02\0\x01Qa\x12\x87V[a$\x1D\x81a\x02 \x01Qa\x12\x87V[a$+\x81a\x02@\x01Qa\x12\x87V[a$9\x81a\x02`\x01Qa\x12\x87V[a$G\x81a\x02\x80\x01Qa\x12\x87V[a$U\x81a\x02\xA0\x01Qa\x12\x87V[a\x12R\x81a\x02\xC0\x01Qa\x12\x87V[a$kaQvV[\x83` \x01Q\x83Q\x14a$\x90W`@Qc \xFA\x9D\x89`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a$\x9D\x85\x85\x85a+\xA5V[\x90P`\0a$\xAE\x86`\0\x01Qa.\xC4V[\x90P`\0a$\xC1\x82\x84`\xA0\x01Q\x88a2\x8FV[`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x91\x92P`\0\x91\x90` \x82\x01a\x03\xC0\x806\x837PP`@\x80Q`\x1E\x80\x82Ra\x03\xE0\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[a%\x0CaQ\xB0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a%\x04W\x90PP\x90P`\0a%1\x8A\x85\x8A\x89\x87\x87a2\xEFV[`\xA0\x87\x01Q``\x87\x01Q\x91\x92P\x90`\0\x80Q` a]\xBC\x839\x81Q\x91R`\0\x81\x83\x85\t`@\x80Qa\x01\0\x81\x01\x82R`\xE0\x9C\x8D\x01Q\x81R` \x81\x01\x96\x90\x96R\x85\x01RPPP``\x81\x01\x91\x90\x91R`\x80\x81\x01\x92\x90\x92R`\xA0\x82\x01Ra\x01`\x86\x01Q`\xC0\x82\x01Ra\x01\x80\x90\x95\x01Q\x92\x85\x01\x92\x90\x92RP\x91\x94\x93PPPPV[`\0`\0\x80Q` a]\xBC\x839\x81Q\x91Ra%\xC6aQ\xB0V[a%\xCEaQ\xB0V[`@\x80Q`\x02\x80\x82R``\x82\x01\x83R`\0\x92` \x83\x01\x90\x806\x837PP`@\x80Q`\x02\x80\x82R``\x82\x01\x90\x92R\x92\x93P`\0\x92\x91P` \x82\x01[a&\x10aQ\xB0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a&\x08W\x90PP\x90P`\0`\x01\x90P\x80\x83`\0\x81Q\x81\x10a&?Wa&?aYQV[` \x02` \x01\x01\x81\x81RPP\x87`\xC0\x01Q\x82`\0\x81Q\x81\x10a&cWa&caYQV[` \x02` \x01\x01\x81\x90RP\x87`\0\x01Q\x83`\x01\x81Q\x81\x10a&\x86Wa&\x86aYQV[` \x02` \x01\x01\x81\x81RPP\x87`\xE0\x01Q\x82`\x01\x81Q\x81\x10a&\xAAWa&\xAAaYQV[` \x02` \x01\x01\x81\x90RPa&\xBF\x82\x84a3$V[`\x80\x89\x01QQ\x90\x95P``\x93P\x83\x92P\x90P`\0a&\xDE\x82`\x02aY\xCAV[a&\xE9\x90`\x01aY\xCAV[\x90P\x80`\x01`\x01`@\x1B\x03\x81\x11\x15a'\x03Wa'\x03aRsV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a',W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x80`\x01`\x01`@\x1B\x03\x81\x11\x15a'GWa'GaRsV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a'\x80W\x81` \x01[a'maQ\xB0V[\x81R` \x01\x90`\x01\x90\x03\x90\x81a'eW\x90P[P\x92PPP`\0\x80`\0[\x89`\x80\x01QQ\x81\x10\x15a($W\x89`\x80\x01Q\x81\x81Q\x81\x10a'\xAEWa'\xAEaYQV[` \x02` \x01\x01Q\x85\x83\x81Q\x81\x10a'\xC8Wa'\xC8aYQV[` \x02` \x01\x01\x81\x81RPP\x89`\xA0\x01Q\x81\x81Q\x81\x10a'\xEAWa'\xEAaYQV[` \x02` \x01\x01Q\x84\x83\x81Q\x81\x10a(\x04Wa(\x04aYQV[` \x90\x81\x02\x91\x90\x91\x01\x01Ra(\x1A`\x01\x83aY\xCAV[\x91P`\x01\x01a'\x8BV[P\x88` \x01Q\x84\x82\x81Q\x81\x10a(a*\xC5V[``\x91P[P\x91P\x91Pa*\xD5\x85\x83\x83a6\x86V[\x95\x94PPPPPV[4\x15a\rOW`@Qc\xB3\x98\x97\x9F`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\0a+\x07a\x18|V[T`\x01`@\x1B\x90\x04`\xFF\x16\x91\x90PV[`\0`\0\x80Q` a]<\x839\x81Q\x91Ra+1\x83a6\xD9V[\x15a+;WPPPV[\x82Q` \x84\x01Q\x82`\x03\x84\x85\x85\x86\t\x85\t\x08\x83\x82\x83\t\x14\x83\x82\x10\x84\x84\x10\x16\x16\x93PPP\x81a\x17\xAEW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x17`$\x82\x01Rv\x10\x9B\x8C\x8DM\x0E\x88\x1A[\x9D\x98[\x1AY\x08\x11\xCCH\x1C\x1B\xDA[\x9D`J\x1B`D\x82\x01R`d\x01a\t\xDAV[a+\xED`@Q\x80a\x01\0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[`@\x80Q\x80\x82\x01\x90\x91R``\x81R`\0` \x82\x01R`\0\x80Q` a]\xBC\x839\x81Q\x91Ra,\x1C\x82\x87\x87a6\xE8V[\x81Q\x84Qa,)\x90a:\x81V[a,6\x86` \x01Qa:\x81V[a,C\x87`@\x01Qa:\x81V[a,P\x88``\x01Qa:\x81V[a,]\x89`\x80\x01Qa:\x81V[`@Q` \x01a,r\x96\x95\x94\x93\x92\x91\x90aY\xF9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\x8D\x82a:\xF6V[Pa,\x97\x82a:\xF6V[``\x84\x01Ra,\xA5\x82a:\xF6V[`\x80\x84\x01R\x81Q`\xA0\x85\x01Qa,\xBA\x90a:\x81V[`@Q` \x01a,\xCB\x92\x91\x90aZxV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra,\xE6\x82a:\xF6V[\x83R\x81Q`\xC0\x85\x01Qa,\xF8\x90a:\x81V[a-\x05\x86`\xE0\x01Qa:\x81V[a-\x13\x87a\x01\0\x01Qa:\x81V[a-!\x88a\x01 \x01Qa:\x81V[a-/\x89a\x01@\x01Qa:\x81V[`@Q` \x01a-D\x96\x95\x94\x93\x92\x91\x90aY\xF9V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra-_\x82a:\xF6V[`\xA0\x84\x01R\x81Qa\x01\xA0\x85\x01Qa-u\x90a;XV[a-\x83\x86a\x01\xC0\x01Qa;XV[a-\x91\x87a\x01\xE0\x01Qa;XV[a-\x9F\x88a\x02\0\x01Qa;XV[a-\xAD\x89a\x02 \x01Qa;XV[`@Q` \x01a-\xC2\x96\x95\x94\x93\x92\x91\x90aZ\xA7V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x80\x83Ra\x02@\x85\x01Qa-\xE4\x90a;XV[a-\xF2\x86a\x02`\x01Qa;XV[a.\0\x87a\x02\x80\x01Qa;XV[a.\x0E\x88a\x02\xA0\x01Qa;XV[a.\x1C\x89a\x02\xC0\x01Qa;XV[`@Q` \x01a.1\x96\x95\x94\x93\x92\x91\x90aZ\xA7V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra.L\x82a:\xF6V[`\xC0\x84\x01R\x81Qa\x01`\x85\x01Qa.b\x90a:\x81V[a.p\x86a\x01\x80\x01Qa:\x81V[`@Q` \x01a.\x82\x93\x92\x91\x90aZ\xE3V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x82Ra.\x9D\x82a:\xF6V[`\xE0\x84\x01R\x82Q\x81\x81\x80\t\x82\x82\x82\t` \x86\x01\x91\x90\x91R`@\x85\x01RP\x91\x95\x94PPPPPV[a.\xF6`@Q\x80`\xA0\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[\x81b\x01\0\0\x03a/\x8AWP`@\x80Q`\xA0\x81\x01\x82R`\x10\x81R` \x81\x01\x92\x90\x92R\x7F0d\x1E\x0E\x92\xBE\xBE\xF8\x18&\x8Df;\xCA\xD6\xDB\xCF\xD6\xC0\x14\x91p\xF6\xD7\xD3P\xB1\xB1\xFAl\x10\x01\x90\x82\x01R~\xEE\xB2\xCBY\x81\xEDEd\x9A\xBE\xBD\xE0\x81\xDC\xFF\x16\xC8`\x1D\xE44~}\xD1b\x8B\xA2\xDA\xACC\xB7``\x82\x01R\x7F\x0B]V\xB7\x7F\xE7\x04\xE8\xE9#8\xC0\x08/7\xE0\x91\x12d\x14\xC80\xE4\xC6\x92-Z\xC8\x02\xD8B\xD4`\x80\x82\x01R\x90V[\x81b\x02\0\0\x03a0\x1FWP`@\x80Q`\xA0\x81\x01\x82R`\x11\x81R` \x81\x01\x92\x90\x92R\x7F0d6@\xB9\xF8/\x90\xE8;i\x8E^\xA6\x17\x9C|\x05T.\x85\x953\xB4\x8B\x99S\xA2\xF56\x08\x01\x90\x82\x01R\x7F\x1B\xF8-\xEB\xA7\xD7I\x02\xC3p\x8C\xC6\xE7\x0Ea\xF3\x05\x12\xEC\xA9VU!\x0E'nXX\xCE\x8FX\xE5``\x82\x01R\x7F$L\xF0\x10\xC4<\xA8r7\xD8\xB0\x0B\xF9\xDDP\xC4\xC0\x1C\x7F\x08k\xD4\xE8\xC9 \xE7RQ\xD9o\r\"`\x80\x82\x01R\x90V[\x81b\x04\0\0\x03a0\xB4WP`@\x80Q`\xA0\x81\x01\x82R`\x12\x81R` \x81\x01\x92\x90\x92R\x7F0dBY\xCD\x94\xE7\xDDPE\xD7\xA2p\x13\xB7\xFC\xD2\x1C\x9E;\x7F\xA7R\"\xE7\xBD\xA4\x9Br\x9B\x04\x01\x90\x82\x01R\x7F\x19\xDD\xBC\xAF:\x8DF\xC1\\\x01v\xFB\xB5\xB9^M\xC5p\x88\xFF\x13\xF4\xD1\xBD\x84\xC6\xBF\xA5}\xCD\xC0\xE0``\x82\x01R\x7F\x03hS\xF0\x83x\x0E\x87\xF8\xD7\xC7\x1D\x11\x11\x19\xC5}\xBE\x11\x8C\"\xD5\xADpz\x821tf\xC5\x17L`\x80\x82\x01R\x90V[\x81b\x08\0\0\x03a1IWP`@\x80Q`\xA0\x81\x01\x82R`\x13\x81R` \x81\x01\x92\x90\x92R\x7F0dHfWcD\x03\x84K\x0E\xACx\xCA\x88,\xFD(CA\xFC\xB0aZ\x15\xCF\xCD\x17\xB1M\x82\x01\x90\x82\x01R\x7F\"`\xE7$\x84K\xCARQ\x82\x93S\x96\x8EI\x150RXA\x83WG:\\\x1DY\x7Fa?l\xBD``\x82\x01R\x7F\x06\xE4\x02\xC0\xA3\x14\xFBg\xA1\\\xF8\x06fJ\xE1\xB7\"\xDB\xC0\xEF\xE6nl\x81\xD9\x8F\x99$\xCASS!`\x80\x82\x01R\x90V[\x81b\x10\0\0\x03a1\xDEWP`@\x80Q`\xA0\x81\x01\x82R`\x14\x81R` \x81\x01\x92\x90\x92R\x7F0dKl\x9CJr\x16\x9EM\xAA1}%\xF0E\x12\xAE\x15\xC5;4\xE8\xF5\xAC\xD8\xE1U\xD0\xA6\xC1\x01\x90\x82\x01R\x7F&\x12]\xA1\n\x0E\xD0c'P\x8A\xBA\x06\xD1\xE3\x03\xACaf2\xDB\xED4\x9FSB-\xA9S3xW``\x82\x01R\x7F\x10\x0C3-!\0\x89_\xABds\xBC,Q\xBF\xCAR\x1FE\xCB;\xAC\xA6&\x08R\xA8\xFD\xE2l\x91\xF3`\x80\x82\x01R\x90V[\x81` \x03a2qWP`@\x80Q`\xA0\x81\x01\x82R`\x05\x81R` \x81\x01\x92\x90\x92R\x7F.\xE1+\xFFJ(\x13(j\x8D\xC3\x88\xCDuM\x9A>\xF2I\x065\xEB\xA5\x0C\xB9\xC2\xE5\xE7P\x80\0\x01\x90\x82\x01R\x7F\t\xC52\xC60k\x93\xD2\x96x \rG\xC0\xB2\xA9\x9C\x18\xD5\x1B\x83\x8E\xEB\x1D>\xEDLS;\xB5\x12\xD0``\x82\x01R\x7F'$q6\x03\xBF\xBDy\n\xEA\xF3\xE7\xDF%\xD8\xE7\xEF\x8F1\x134\x90[M\x8C\x99\x98\x0C\xF2\x10\x97\x9D`\x80\x82\x01R\x90V[`@Qc\xE2\xEF\t\xE5`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[\x91\x90PV[a2\xB3`@Q\x80``\x01`@R\x80`\0\x81R` \x01`\0\x81R` \x01`\0\x81RP\x90V[a2\xBD\x84\x84a<\x8FV[\x80\x82Ra2\xCD\x90\x85\x90\x85\x90a<\xE3V[` \x82\x01R\x80Qa2\xE3\x90\x85\x90\x84\x90\x86\x90a=WV[`@\x82\x01R\x93\x92PPPV[`\0\x80a2\xFD\x85\x87\x89a?\x0BV[\x90Pa3\r\x88\x86\x89\x89\x88\x88a?\xF7V[a3\x18\x81\x87\x86aC\x03V[\x98\x97PPPPPPPPV[a3,aQ\xB0V[\x82Q\x82Q\x14a3}W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FMSM error: length does not match`D\x82\x01R`d\x01a\t\xDAV[a3\xBB\x83`\0\x81Q\x81\x10a3\x93Wa3\x93aYQV[` \x02` \x01\x01Q\x83`\0\x81Q\x81\x10a3\xAEWa3\xAEaYQV[` \x02` \x01\x01QaCSV[\x90P`\x01[\x82Q\x81\x10\x15a4\x0BWa4\x01\x82a3\xFC\x86\x84\x81Q\x81\x10a3\xE2Wa3\xE2aYQV[` \x02` \x01\x01Q\x86\x85\x81Q\x81\x10a3\xAEWa3\xAEaYQV[aC\xE7V[\x91P`\x01\x01a3\xC0V[P\x92\x91PPV[`\0a4,`\0\x80Q` a]\xBC\x839\x81Q\x91R\x83a[\x0EWa>\x0EaRsV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a>7W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P\x88\x8B\x85\t\x93P`\x01\x92P`\0[\x88\x81\x10\x15a>|W` \x81\x02` \x84\x01\x01Q\x95P\x89\x8D\x87\x8C\x03\x08\x96P\x89\x87\x85\t` \x82\x81\x02\x84\x01\x01\x88\x90R\x93P`\x01\x01a>GV[Pa>\x86\x83aE\xB8V[\x92P`\0[\x88\x81\x10\x15a>\xF9W` \x81\x02` \x84\x01\x01Q\x95P\x89\x86\x86\t\x97P\x89\x84\x89\t\x97P`\0[\x89\x81\x10\x15a>\xD8W\x80\x82\x14a>\xD0W` \x81\x02` \x84\x01\x01Q\x97P\x8A\x88\x8A\t\x98P[`\x01\x01a>\xAEV[P` \x81\x02` \x8F\x01\x01Q\x95P\x89\x86\x89\t\x97P\x89\x88\x8C\x08\x9AP`\x01\x01a>\x8BV[PPPPPPPPPP\x94\x93PPPPV[`\0\x80`\0\x80Q` a]\xBC\x839\x81Q\x91R\x90P`\0\x83` \x01Q\x90P`\0\x84`@\x01Q\x90P`\0`\x01\x90P``\x88\x01Q`\x80\x89\x01Qa\x01\xA0\x89\x01Qa\x02@\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xC0\x89\x01Qa\x02`\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x01\xE0\x89\x01Qa\x02\x80\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02\0\x89\x01Qa\x02\xA0\x8A\x01Q\x87\x88\x89\x83\x87\t\x85\x85\x01\x08\x86\t\x94PPPa\x02 \x89\x01Q\x91Pa\x02\xC0\x89\x01Q\x86\x87\x82\x89\x85\x87\x08\t\x85\t\x93PPPP\x87Q` \x89\x01Q\x85\x86\x86\x83\t\x87\x03\x85\x08\x96PP\x84\x85\x83\x83\t\x86\x03\x87\x08\x99\x98PPPPPPPPPV[a@\x05\x86\x86\x86\x86\x85\x87aG#V[`\xC0\x85\x01Q\x82Q`\0\x80Q` a]\xBC\x839\x81Q\x91R\x91\x90\x81\x90\x81\x90\x86\x90`\x14\x90\x81\x10a@4Wa@4aYQV[` \x02` \x01\x01\x81\x81RPP\x85`\0\x01Q\x84`\x14\x81Q\x81\x10a@XWa@XaYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x15\x81Q\x81\x10a@}Wa@}aYQV[` \x02` \x01\x01\x81\x81RPP\x85` \x01Q\x84`\x15\x81Q\x81\x10a@\xA1Wa@\xA1aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x16\x81Q\x81\x10a@\xC6Wa@\xC6aYQV[` \x02` \x01\x01\x81\x81RPP\x85`@\x01Q\x84`\x16\x81Q\x81\x10a@\xEAWa@\xEAaYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x17\x81Q\x81\x10aA\x0FWaA\x0FaYQV[` \x02` \x01\x01\x81\x81RPP\x85``\x01Q\x84`\x17\x81Q\x81\x10aA3WaA3aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x18\x81Q\x81\x10aAXWaAXaYQV[` \x02` \x01\x01\x81\x81RPP\x85`\x80\x01Q\x84`\x18\x81Q\x81\x10aA|WaA|aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x19\x81Q\x81\x10aA\xA1WaA\xA1aYQV[` \x02` \x01\x01\x81\x81RPP\x88`@\x01Q\x84`\x19\x81Q\x81\x10aA\xC5WaA\xC5aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1A\x81Q\x81\x10aA\xEAWaA\xEAaYQV[` \x02` \x01\x01\x81\x81RPP\x88``\x01Q\x84`\x1A\x81Q\x81\x10aB\x0EWaB\x0EaYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1B\x81Q\x81\x10aB3WaB3aYQV[` \x02` \x01\x01\x81\x81RPP\x88`\x80\x01Q\x84`\x1B\x81Q\x81\x10aBWWaBWaYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x80\x85`\x1C\x81Q\x81\x10aB|WaB|aYQV[` \x02` \x01\x01\x81\x81RPP\x88`\xA0\x01Q\x84`\x1C\x81Q\x81\x10aB\xA0WaB\xA0aYQV[` \x02` \x01\x01\x81\x90RP\x82\x82\x82\t\x90P\x87`\xE0\x01Q\x85`\x1D\x81Q\x81\x10aB\xC9WaB\xC9aYQV[` \x02` \x01\x01\x81\x81RPP\x85`\xA0\x01Q\x84`\x1D\x81Q\x81\x10aB\xEDWaB\xEDaYQV[` \x02` \x01\x01\x81\x90RPPPPPPPPPPV[`\0\x80Q` a]\xBC\x839\x81Q\x91R\x83\x81\x03\x90`\0[`\n\x81\x10\x15aCJW` `\x15\x82\x01\x02\x84\x01Q` \x82\x02a\x01\xA0\x01\x86\x01Q\x83\x84\x82\x84\t\x86\x08\x94PPP`\x01\x01aC\x19V[PP\x93\x92PPPV[aC[aQ\xB0V[aCcaQ\xCAV[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x81\x01\x83\x90R`\0``\x83`\x80\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80aC\x95W`\0\x80\xFD[P\x80aC\xDFW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01RxBn254: scalar mul failed!`8\x1B`D\x82\x01R`d\x01a\t\xDAV[PP\x92\x91PPV[aC\xEFaQ\xB0V[aC\xF7aQ\xE8V[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x83\x01R\x83\x01Q``\x80\x83\x01\x91\x90\x91R`\0\x90\x83`\xC0\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80aD4W`\0\x80\xFD[P\x80aC\xDFW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: group addition failed!\0\0\0`D\x82\x01R`d\x01a\t\xDAV[\x80Q\x15aD\x92W\x80Q\x80\x82` \x01\xFD[`@Qc\n\x12\xF5!`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x81aD\xB9\x81`\x1FaY\xCAV[\x10\x15aD\xF8W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x0E`$\x82\x01Rmslice_overflow`\x90\x1B`D\x82\x01R`d\x01a\t\xDAV[aE\x02\x82\x84aY\xCAV[\x84Q\x10\x15aEFW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x11`$\x82\x01Rpslice_outOfBounds`x\x1B`D\x82\x01R`d\x01a\t\xDAV[``\x82\x15\x80\x15aEeW`@Q\x91P`\0\x82R` \x82\x01`@RaE\xAFV[`@Q\x91P`\x1F\x84\x16\x80\x15` \x02\x81\x84\x01\x01\x85\x81\x01\x87\x83\x15` \x02\x84\x8B\x01\x01\x01[\x81\x83\x10\x15aE\x9EW\x80Q\x83R` \x92\x83\x01\x92\x01aE\x86V[PP\x85\x84R`\x1F\x01`\x1F\x19\x16`@RP[P\x94\x93PPPPV[`\0\x80`\0`\0\x80Q` a]\xBC\x839\x81Q\x91R\x90P`@Q` \x81R` \x80\x82\x01R` `@\x82\x01R\x84``\x82\x01R`\x02\x82\x03`\x80\x82\x01R\x81`\xA0\x82\x01R` `\0`\xC0\x83`\x05Z\xFA\x92PP`\0Q\x92P\x81aFWW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1D`$\x82\x01R\x7FBn254: pow precompile failed!\0\0\0`D\x82\x01R`d\x01a\t\xDAV[PP\x91\x90PV[``\x82` \x01Q\x82\x11\x15aF\x85W`@Qc\x8C^\x11\xF1`\xE0\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[``\x83\x01Q`\x01`\0\x80Q` a]\xBC\x839\x81Q\x91R\x84`\x01`\x01`@\x1B\x03\x81\x11\x15aF\xB3WaF\xB3aRsV[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15aF\xDCW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x93P\x84\x15\x19\x15a<\xDAW` \x84\x01\x85` \x02\x81\x01`\x01\x82R` \x82\x01\x91P[\x80\x82\x10\x15aG\x18W\x82\x85\x85\t\x93P\x83\x82R` \x82\x01\x91PaF\xFCV[PPPPP\x92\x91PPV[`\0\x80`\0\x80`\0\x80`\0\x80Q` a]\xBC\x839\x81Q\x91R\x90P\x80` \x8B\x01Q` \x8D\x01Q\t\x95P\x8AQ\x93P\x80`\xA0\x8C\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` a^\\\x839\x81Q\x91R\x84\t\x91P\x80a\x01\xC0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` a]\xFC\x839\x81Q\x91R\x84\t\x91P\x80a\x01\xE0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` a^<\x839\x81Q\x91R\x84\t\x91P\x80a\x02\0\x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80`\0\x80Q` a]|\x839\x81Q\x91R\x84\t\x91P\x80a\x02 \x8A\x01Q\x83\x08\x91P\x80`\x80\x8C\x01Q\x83\x08\x91P\x80\x84\x83\t\x93P\x80\x84\x87\x08\x95P\x88`\xA0\x01Q\x88`\0\x81Q\x81\x10aHRWaHRaYQV[` \x02` \x01\x01\x81\x90RP\x85\x87`\0\x81Q\x81\x10aHqWaHqaYQV[` \x02` \x01\x01\x81\x81RPP\x80``\x8C\x01Q\x8CQ\t\x94P\x80a\x02\xC0\x8A\x01Q\x86\t\x94P\x80a\x02@\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xA0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02`\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xC0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02\x80\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x01\xE0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x80a\x02\xA0\x8A\x01Q``\x8D\x01Q\t\x92P\x80a\x02\0\x8A\x01Q\x84\x08\x92P\x80`\x80\x8C\x01Q\x84\x08\x92P\x80\x83\x86\t\x94P\x8B`\xC0\x01Q\x88`\x01\x81Q\x81\x10aISWaISaYQV[` \x90\x81\x02\x91\x90\x91\x01\x01RaIh\x85\x82aYgV[\x87`\x01\x81Q\x81\x10aI{WaI{aYQV[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xA0\x01Q\x87`\x02\x81Q\x81\x10aI\xA0WaI\xA0aYQV[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xC0\x01Q\x87`\x03\x81Q\x81\x10aI\xC5WaI\xC5aYQV[` \x02` \x01\x01\x81\x81RPP\x88a\x01\xE0\x01Q\x87`\x04\x81Q\x81\x10aI\xEAWaI\xEAaYQV[` \x02` \x01\x01\x81\x81RPP\x88a\x02\0\x01Q\x87`\x05\x81Q\x81\x10aJ\x0FWaJ\x0FaYQV[` \x02` \x01\x01\x81\x81RPP\x8B`\xE0\x01Q\x88`\x02\x81Q\x81\x10aJ3WaJ3aYQV[` \x02` \x01\x01\x81\x90RP\x8Ba\x01\0\x01Q\x88`\x03\x81Q\x81\x10aJWWaJWaYQV[` \x02` \x01\x01\x81\x90RP\x8Ba\x01 \x01Q\x88`\x04\x81Q\x81\x10aJ{WaJ{aYQV[` \x02` \x01\x01\x81\x90RP\x8Ba\x01@\x01Q\x88`\x05\x81Q\x81\x10aJ\x9FWaJ\x9FaYQV[` \x02` \x01\x01\x81\x90RP\x80a\x01\xC0\x8A\x01Qa\x01\xA0\x8B\x01Q\t\x92P\x82\x87`\x06\x81Q\x81\x10aJ\xCEWaJ\xCEaYQV[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01`\x01Q\x88`\x06\x81Q\x81\x10aJ\xF3WaJ\xF3aYQV[` \x02` \x01\x01\x81\x90RP\x80a\x02\0\x8A\x01Qa\x01\xE0\x8B\x01Q\t\x92P\x82\x87`\x07\x81Q\x81\x10aK\"WaK\"aYQV[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\x80\x01Q\x88`\x07\x81Q\x81\x10aKGWaKGaYQV[` \x02` \x01\x01\x81\x90RPa\x01\xA0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\x08\x81Q\x81\x10aK\x80WaK\x80aYQV[` \x02` \x01\x01\x81\x81RPP\x8Ba\x01\xE0\x01Q\x88`\x08\x81Q\x81\x10aK\xA5WaK\xA5aYQV[` \x02` \x01\x01\x81\x90RPa\x01\xC0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\t\x81Q\x81\x10aK\xDEWaK\xDEaYQV[` \x02` \x01\x01\x81\x81RPP\x8Ba\x02\0\x01Q\x88`\t\x81Q\x81\x10aL\x03WaL\x03aYQV[` \x02` \x01\x01\x81\x90RPa\x01\xE0\x89\x01Q\x92P\x80\x83\x84\t\x91P\x80\x82\x83\t\x91P\x80\x82\x84\t\x92P\x82\x87`\n\x81Q\x81\x10aL v\xCC75\xA9 \xA3\xCAP]8+\xBC0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE3\x1E\xE6x\xA0G\nu\xA6\xEA\xA8\xFE\x83p`I\x8B\xA8(\xA3p;1\x1D\x0Fw\xF0\x10BJ\xFE\xB0%\xF3\xF7\xA9\xFE6O\xAA\xB9;!m\xA5\n2\x14\x15O\"\xA0\xA2\xB4\x15\xB2:\x84\xC8\x16\x9E\x8Bcn\xE4 B\xA5\x87\xA9\x0C\x18{\n\x08|\x03\xE2\x9C\x96\x8B\x95\x0B\x1D\xB2m\\\x82\xD6f\x90Zh\x95y\x0C\n/\x8D\xD1\xF1\xA7X ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([84, 100, 96, 133], ()) + .expect("method not found (this should never happen)") + } + ///Calls the contract's `getHotShotCommitment` (0x8584d23f) function + pub fn get_hot_shot_commitment( + &self, + hot_shot_block_height: ::ethers::core::types::U256, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([133, 132, 210, 63], hot_shot_block_height) + .expect("method not found (this should never happen)") + } + ///Calls the contract's `getStateUpdateBlockNumbersCount` (0x7053fc51) function + pub fn get_state_update_block_numbers_count( + &self, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([112, 83, 252, 81], ()) + .expect("method not found (this should never happen)") + } ///Calls the contract's `getVersion` (0x0d8e6e2c) function pub fn get_version(&self) -> ::ethers::contract::builders::ContractCall { self.0 .method_hash([13, 142, 110, 44], ()) .expect("method not found (this should never happen)") } + ///Calls the contract's `hotShotCommitments` (0xdb13b60a) function + pub fn hot_shot_commitments( + &self, + p0: ::ethers::core::types::U256, + ) -> ::ethers::contract::builders::ContractCall + { + self.0 + .method_hash([219, 19, 182, 10], p0) + .expect("method not found (this should never happen)") + } ///Calls the contract's `initialize` (0xa244d596) function pub fn initialize( &self, @@ -1159,6 +1398,16 @@ pub mod light_client_mock { .method_hash([162, 68, 213, 150], (genesis, num_blocks_per_epoch, owner)) .expect("method not found (this should never happen)") } + ///Calls the contract's `lagOverEscapeHatchThreshold` (0xe0303301) function + pub fn lag_over_escape_hatch_threshold( + &self, + block_number: ::ethers::core::types::U256, + threshold: ::ethers::core::types::U256, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([224, 48, 51, 1], (block_number, threshold)) + .expect("method not found (this should never happen)") + } ///Calls the contract's `newFinalizedState` (0x409939b7) function pub fn new_finalized_state( &self, @@ -1223,6 +1472,15 @@ pub mod light_client_mock { .method_hash([32, 42, 10, 219], (state,)) .expect("method not found (this should never happen)") } + ///Calls the contract's `setHotShotCommitments` (0x530ca78f) function + pub fn set_hot_shot_commitments( + &self, + values: ::std::vec::Vec, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([83, 12, 167, 143], values) + .expect("method not found (this should never happen)") + } ///Calls the contract's `setPermissionedProver` (0x013fa5fc) function pub fn set_permissioned_prover( &self, @@ -1232,6 +1490,24 @@ pub mod light_client_mock { .method_hash([1, 63, 165, 252], prover) .expect("method not found (this should never happen)") } + ///Calls the contract's `setStateUpdateBlockNumbers` (0x3919340f) function + pub fn set_state_update_block_numbers( + &self, + values: ::std::vec::Vec<::ethers::core::types::U256>, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([57, 25, 52, 15], values) + .expect("method not found (this should never happen)") + } + ///Calls the contract's `stateUpdateBlockNumbers` (0xa51e6fea) function + pub fn state_update_block_numbers( + &self, + p0: ::ethers::core::types::U256, + ) -> ::ethers::contract::builders::ContractCall { + self.0 + .method_hash([165, 30, 111, 234], p0) + .expect("method not found (this should never happen)") + } ///Calls the contract's `states` (0x7f17baad) function pub fn states( &self, @@ -1430,6 +1706,24 @@ pub mod light_client_mock { )] #[etherror(name = "FailedInnerCall", abi = "FailedInnerCall()")] pub struct FailedInnerCall; + ///Custom Error type `InsufficientSnapshotHistory` with signature `InsufficientSnapshotHistory()` and selector `0xb0b43877` + #[derive( + Clone, + ::ethers::contract::EthError, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[etherror( + name = "InsufficientSnapshotHistory", + abi = "InsufficientSnapshotHistory()" + )] + pub struct InsufficientSnapshotHistory; ///Custom Error type `InvalidAddress` with signature `InvalidAddress()` and selector `0xe6c4247b` #[derive( Clone, @@ -1460,6 +1754,24 @@ pub mod light_client_mock { )] #[etherror(name = "InvalidArgs", abi = "InvalidArgs()")] pub struct InvalidArgs; + ///Custom Error type `InvalidHotShotBlockForCommitmentCheck` with signature `InvalidHotShotBlockForCommitmentCheck()` and selector `0x615a9264` + #[derive( + Clone, + ::ethers::contract::EthError, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[etherror( + name = "InvalidHotShotBlockForCommitmentCheck", + abi = "InvalidHotShotBlockForCommitmentCheck()" + )] + pub struct InvalidHotShotBlockForCommitmentCheck; ///Custom Error type `InvalidInitialization` with signature `InvalidInitialization()` and selector `0xf92ee8a9` #[derive( Clone, @@ -1736,8 +2048,10 @@ pub mod light_client_mock { ERC1967InvalidImplementation(ERC1967InvalidImplementation), ERC1967NonPayable(ERC1967NonPayable), FailedInnerCall(FailedInnerCall), + InsufficientSnapshotHistory(InsufficientSnapshotHistory), InvalidAddress(InvalidAddress), InvalidArgs(InvalidArgs), + InvalidHotShotBlockForCommitmentCheck(InvalidHotShotBlockForCommitmentCheck), InvalidInitialization(InvalidInitialization), InvalidPolyEvalArgs(InvalidPolyEvalArgs), InvalidProof(InvalidProof), @@ -1784,12 +2098,24 @@ pub mod light_client_mock { if let Ok(decoded) = ::decode(data) { return Ok(Self::FailedInnerCall(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::InsufficientSnapshotHistory(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::InvalidAddress(decoded)); } if let Ok(decoded) = ::decode(data) { return Ok(Self::InvalidArgs(decoded)); } + if let Ok(decoded) = + ::decode( + data, + ) + { + return Ok(Self::InvalidHotShotBlockForCommitmentCheck(decoded)); + } if let Ok(decoded) = ::decode(data) { @@ -1872,8 +2198,14 @@ pub mod light_client_mock { } Self::ERC1967NonPayable(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::FailedInnerCall(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::InsufficientSnapshotHistory(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::InvalidAddress(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::InvalidArgs(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::InvalidHotShotBlockForCommitmentCheck(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::InvalidInitialization(element) => { ::ethers::core::abi::AbiEncode::encode(element) } @@ -1934,12 +2266,20 @@ pub mod light_client_mock { == ::selector() => { true } + _ if selector + == ::selector() => { + true + } _ if selector == ::selector() => { true } _ if selector == ::selector() => true, + _ if selector + == ::selector() => { + true + } _ if selector == ::selector() => { true @@ -2013,8 +2353,12 @@ pub mod light_client_mock { } Self::ERC1967NonPayable(element) => ::core::fmt::Display::fmt(element, f), Self::FailedInnerCall(element) => ::core::fmt::Display::fmt(element, f), + Self::InsufficientSnapshotHistory(element) => ::core::fmt::Display::fmt(element, f), Self::InvalidAddress(element) => ::core::fmt::Display::fmt(element, f), Self::InvalidArgs(element) => ::core::fmt::Display::fmt(element, f), + Self::InvalidHotShotBlockForCommitmentCheck(element) => { + ::core::fmt::Display::fmt(element, f) + } Self::InvalidInitialization(element) => ::core::fmt::Display::fmt(element, f), Self::InvalidPolyEvalArgs(element) => ::core::fmt::Display::fmt(element, f), Self::InvalidProof(element) => ::core::fmt::Display::fmt(element, f), @@ -2064,6 +2408,11 @@ pub mod light_client_mock { Self::FailedInnerCall(value) } } + impl ::core::convert::From for LightClientMockErrors { + fn from(value: InsufficientSnapshotHistory) -> Self { + Self::InsufficientSnapshotHistory(value) + } + } impl ::core::convert::From for LightClientMockErrors { fn from(value: InvalidAddress) -> Self { Self::InvalidAddress(value) @@ -2074,6 +2423,11 @@ pub mod light_client_mock { Self::InvalidArgs(value) } } + impl ::core::convert::From for LightClientMockErrors { + fn from(value: InvalidHotShotBlockForCommitmentCheck) -> Self { + Self::InvalidHotShotBlockForCommitmentCheck(value) + } + } impl ::core::convert::From for LightClientMockErrors { fn from(value: InvalidInitialization) -> Self { Self::InvalidInitialization(value) @@ -2558,6 +2912,59 @@ pub mod light_client_mock { )] #[ethcall(name = "getGenesisState", abi = "getGenesisState()")] pub struct GetGenesisStateCall; + ///Container type for all input parameters for the `getHotShotBlockCommitmentsCount` function with signature `getHotShotBlockCommitmentsCount()` and selector `0x54646085` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "getHotShotBlockCommitmentsCount", + abi = "getHotShotBlockCommitmentsCount()" + )] + pub struct GetHotShotBlockCommitmentsCountCall; + ///Container type for all input parameters for the `getHotShotCommitment` function with signature `getHotShotCommitment(uint256)` and selector `0x8584d23f` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall(name = "getHotShotCommitment", abi = "getHotShotCommitment(uint256)")] + pub struct GetHotShotCommitmentCall { + pub hot_shot_block_height: ::ethers::core::types::U256, + } + ///Container type for all input parameters for the `getStateUpdateBlockNumbersCount` function with signature `getStateUpdateBlockNumbersCount()` and selector `0x7053fc51` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "getStateUpdateBlockNumbersCount", + abi = "getStateUpdateBlockNumbersCount()" + )] + pub struct GetStateUpdateBlockNumbersCountCall; ///Container type for all input parameters for the `getVersion` function with signature `getVersion()` and selector `0x0d8e6e2c` #[derive( Clone, @@ -2573,6 +2980,21 @@ pub mod light_client_mock { )] #[ethcall(name = "getVersion", abi = "getVersion()")] pub struct GetVersionCall; + ///Container type for all input parameters for the `hotShotCommitments` function with signature `hotShotCommitments(uint256)` and selector `0xdb13b60a` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall(name = "hotShotCommitments", abi = "hotShotCommitments(uint256)")] + pub struct HotShotCommitmentsCall(pub ::ethers::core::types::U256); ///Container type for all input parameters for the `initialize` function with signature `initialize((uint64,uint64,uint256,uint256,uint256,uint256,uint256,uint256),uint32,address)` and selector `0xa244d596` #[derive( Clone, @@ -2595,6 +3017,27 @@ pub mod light_client_mock { pub num_blocks_per_epoch: u32, pub owner: ::ethers::core::types::Address, } + ///Container type for all input parameters for the `lagOverEscapeHatchThreshold` function with signature `lagOverEscapeHatchThreshold(uint256,uint256)` and selector `0xe0303301` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "lagOverEscapeHatchThreshold", + abi = "lagOverEscapeHatchThreshold(uint256,uint256)" + )] + pub struct LagOverEscapeHatchThresholdCall { + pub block_number: ::ethers::core::types::U256, + pub threshold: ::ethers::core::types::U256, + } ///Container type for all input parameters for the `newFinalizedState` function with signature `newFinalizedState((uint64,uint64,uint256,uint256,uint256,uint256,uint256,uint256),((uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256))` and selector `0x409939b7` #[derive( Clone, @@ -2726,6 +3169,26 @@ pub mod light_client_mock { pub struct SetFinalizedStateCall { pub state: LightClientState, } + ///Container type for all input parameters for the `setHotShotCommitments` function with signature `setHotShotCommitments((uint64,uint256)[])` and selector `0x530ca78f` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "setHotShotCommitments", + abi = "setHotShotCommitments((uint64,uint256)[])" + )] + pub struct SetHotShotCommitmentsCall { + pub values: ::std::vec::Vec, + } ///Container type for all input parameters for the `setPermissionedProver` function with signature `setPermissionedProver(address)` and selector `0x013fa5fc` #[derive( Clone, @@ -2743,6 +3206,44 @@ pub mod light_client_mock { pub struct SetPermissionedProverCall { pub prover: ::ethers::core::types::Address, } + ///Container type for all input parameters for the `setStateUpdateBlockNumbers` function with signature `setStateUpdateBlockNumbers(uint256[])` and selector `0x3919340f` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "setStateUpdateBlockNumbers", + abi = "setStateUpdateBlockNumbers(uint256[])" + )] + pub struct SetStateUpdateBlockNumbersCall { + pub values: ::std::vec::Vec<::ethers::core::types::U256>, + } + ///Container type for all input parameters for the `stateUpdateBlockNumbers` function with signature `stateUpdateBlockNumbers(uint256)` and selector `0xa51e6fea` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethcall( + name = "stateUpdateBlockNumbers", + abi = "stateUpdateBlockNumbers(uint256)" + )] + pub struct StateUpdateBlockNumbersCall(pub ::ethers::core::types::U256); ///Container type for all input parameters for the `states` function with signature `states(uint32)` and selector `0x7f17baad` #[derive( Clone, @@ -2840,8 +3341,13 @@ pub mod light_client_mock { FrozenThreshold(FrozenThresholdCall), GetFinalizedState(GetFinalizedStateCall), GetGenesisState(GetGenesisStateCall), + GetHotShotBlockCommitmentsCount(GetHotShotBlockCommitmentsCountCall), + GetHotShotCommitment(GetHotShotCommitmentCall), + GetStateUpdateBlockNumbersCount(GetStateUpdateBlockNumbersCountCall), GetVersion(GetVersionCall), + HotShotCommitments(HotShotCommitmentsCall), Initialize(InitializeCall), + LagOverEscapeHatchThreshold(LagOverEscapeHatchThresholdCall), NewFinalizedState(NewFinalizedStateCall), Owner(OwnerCall), PermissionedProver(PermissionedProverCall), @@ -2850,7 +3356,10 @@ pub mod light_client_mock { RenounceOwnership(RenounceOwnershipCall), SetCurrentEpoch(SetCurrentEpochCall), SetFinalizedState(SetFinalizedStateCall), + SetHotShotCommitments(SetHotShotCommitmentsCall), SetPermissionedProver(SetPermissionedProverCall), + SetStateUpdateBlockNumbers(SetStateUpdateBlockNumbersCall), + StateUpdateBlockNumbers(StateUpdateBlockNumbersCall), States(StatesCall), TransferOwnership(TransferOwnershipCall), UpgradeToAndCall(UpgradeToAndCallCall), @@ -2906,12 +3415,41 @@ pub mod light_client_mock { { return Ok(Self::GetGenesisState(decoded)); } + if let Ok(decoded) = + ::decode( + data, + ) + { + return Ok(Self::GetHotShotBlockCommitmentsCount(decoded)); + } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::GetHotShotCommitment(decoded)); + } + if let Ok(decoded) = + ::decode( + data, + ) + { + return Ok(Self::GetStateUpdateBlockNumbersCount(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::GetVersion(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::HotShotCommitments(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::Initialize(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::LagOverEscapeHatchThreshold(decoded)); + } if let Ok(decoded) = ::decode(data) { @@ -2949,11 +3487,26 @@ pub mod light_client_mock { { return Ok(Self::SetFinalizedState(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::SetHotShotCommitments(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::SetPermissionedProver(decoded)); } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::SetStateUpdateBlockNumbers(decoded)); + } + if let Ok(decoded) = + ::decode(data) + { + return Ok(Self::StateUpdateBlockNumbers(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::States(decoded)); } @@ -3000,8 +3553,23 @@ pub mod light_client_mock { Self::FrozenThreshold(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::GetFinalizedState(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::GetGenesisState(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::GetHotShotBlockCommitmentsCount(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } + Self::GetHotShotCommitment(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } + Self::GetStateUpdateBlockNumbersCount(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::GetVersion(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::HotShotCommitments(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::Initialize(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::LagOverEscapeHatchThreshold(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::NewFinalizedState(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::Owner(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::PermissionedProver(element) => { @@ -3014,9 +3582,18 @@ pub mod light_client_mock { Self::RenounceOwnership(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::SetCurrentEpoch(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::SetFinalizedState(element) => ::ethers::core::abi::AbiEncode::encode(element), + Self::SetHotShotCommitments(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::SetPermissionedProver(element) => { ::ethers::core::abi::AbiEncode::encode(element) } + Self::SetStateUpdateBlockNumbers(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } + Self::StateUpdateBlockNumbers(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::States(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::TransferOwnership(element) => ::ethers::core::abi::AbiEncode::encode(element), Self::UpgradeToAndCall(element) => ::ethers::core::abi::AbiEncode::encode(element), @@ -3041,8 +3618,17 @@ pub mod light_client_mock { Self::FrozenThreshold(element) => ::core::fmt::Display::fmt(element, f), Self::GetFinalizedState(element) => ::core::fmt::Display::fmt(element, f), Self::GetGenesisState(element) => ::core::fmt::Display::fmt(element, f), + Self::GetHotShotBlockCommitmentsCount(element) => { + ::core::fmt::Display::fmt(element, f) + } + Self::GetHotShotCommitment(element) => ::core::fmt::Display::fmt(element, f), + Self::GetStateUpdateBlockNumbersCount(element) => { + ::core::fmt::Display::fmt(element, f) + } Self::GetVersion(element) => ::core::fmt::Display::fmt(element, f), + Self::HotShotCommitments(element) => ::core::fmt::Display::fmt(element, f), Self::Initialize(element) => ::core::fmt::Display::fmt(element, f), + Self::LagOverEscapeHatchThreshold(element) => ::core::fmt::Display::fmt(element, f), Self::NewFinalizedState(element) => ::core::fmt::Display::fmt(element, f), Self::Owner(element) => ::core::fmt::Display::fmt(element, f), Self::PermissionedProver(element) => ::core::fmt::Display::fmt(element, f), @@ -3051,7 +3637,10 @@ pub mod light_client_mock { Self::RenounceOwnership(element) => ::core::fmt::Display::fmt(element, f), Self::SetCurrentEpoch(element) => ::core::fmt::Display::fmt(element, f), Self::SetFinalizedState(element) => ::core::fmt::Display::fmt(element, f), + Self::SetHotShotCommitments(element) => ::core::fmt::Display::fmt(element, f), Self::SetPermissionedProver(element) => ::core::fmt::Display::fmt(element, f), + Self::SetStateUpdateBlockNumbers(element) => ::core::fmt::Display::fmt(element, f), + Self::StateUpdateBlockNumbers(element) => ::core::fmt::Display::fmt(element, f), Self::States(element) => ::core::fmt::Display::fmt(element, f), Self::TransferOwnership(element) => ::core::fmt::Display::fmt(element, f), Self::UpgradeToAndCall(element) => ::core::fmt::Display::fmt(element, f), @@ -3105,16 +3694,41 @@ pub mod light_client_mock { Self::GetGenesisState(value) } } + impl ::core::convert::From for LightClientMockCalls { + fn from(value: GetHotShotBlockCommitmentsCountCall) -> Self { + Self::GetHotShotBlockCommitmentsCount(value) + } + } + impl ::core::convert::From for LightClientMockCalls { + fn from(value: GetHotShotCommitmentCall) -> Self { + Self::GetHotShotCommitment(value) + } + } + impl ::core::convert::From for LightClientMockCalls { + fn from(value: GetStateUpdateBlockNumbersCountCall) -> Self { + Self::GetStateUpdateBlockNumbersCount(value) + } + } impl ::core::convert::From for LightClientMockCalls { fn from(value: GetVersionCall) -> Self { Self::GetVersion(value) } } + impl ::core::convert::From for LightClientMockCalls { + fn from(value: HotShotCommitmentsCall) -> Self { + Self::HotShotCommitments(value) + } + } impl ::core::convert::From for LightClientMockCalls { fn from(value: InitializeCall) -> Self { Self::Initialize(value) } } + impl ::core::convert::From for LightClientMockCalls { + fn from(value: LagOverEscapeHatchThresholdCall) -> Self { + Self::LagOverEscapeHatchThreshold(value) + } + } impl ::core::convert::From for LightClientMockCalls { fn from(value: NewFinalizedStateCall) -> Self { Self::NewFinalizedState(value) @@ -3155,11 +3769,26 @@ pub mod light_client_mock { Self::SetFinalizedState(value) } } + impl ::core::convert::From for LightClientMockCalls { + fn from(value: SetHotShotCommitmentsCall) -> Self { + Self::SetHotShotCommitments(value) + } + } impl ::core::convert::From for LightClientMockCalls { fn from(value: SetPermissionedProverCall) -> Self { Self::SetPermissionedProver(value) } } + impl ::core::convert::From for LightClientMockCalls { + fn from(value: SetStateUpdateBlockNumbersCall) -> Self { + Self::SetStateUpdateBlockNumbers(value) + } + } + impl ::core::convert::From for LightClientMockCalls { + fn from(value: StateUpdateBlockNumbersCall) -> Self { + Self::StateUpdateBlockNumbers(value) + } + } impl ::core::convert::From for LightClientMockCalls { fn from(value: StatesCall) -> Self { Self::States(value) @@ -3297,6 +3926,48 @@ pub mod light_client_mock { Hash, )] pub struct GetGenesisStateReturn(pub LightClientState); + ///Container type for all return fields from the `getHotShotBlockCommitmentsCount` function with signature `getHotShotBlockCommitmentsCount()` and selector `0x54646085` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct GetHotShotBlockCommitmentsCountReturn(pub ::ethers::core::types::U256); + ///Container type for all return fields from the `getHotShotCommitment` function with signature `getHotShotCommitment(uint256)` and selector `0x8584d23f` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct GetHotShotCommitmentReturn(pub HotShotCommitment); + ///Container type for all return fields from the `getStateUpdateBlockNumbersCount` function with signature `getStateUpdateBlockNumbersCount()` and selector `0x7053fc51` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct GetStateUpdateBlockNumbersCountReturn(pub ::ethers::core::types::U256); ///Container type for all return fields from the `getVersion` function with signature `getVersion()` and selector `0x0d8e6e2c` #[derive( Clone, @@ -3315,6 +3986,37 @@ pub mod light_client_mock { pub minor_version: u8, pub patch_version: u8, } + ///Container type for all return fields from the `hotShotCommitments` function with signature `hotShotCommitments(uint256)` and selector `0xdb13b60a` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct HotShotCommitmentsReturn { + pub block_height: u64, + pub block_comm_root: ::ethers::core::types::U256, + } + ///Container type for all return fields from the `lagOverEscapeHatchThreshold` function with signature `lagOverEscapeHatchThreshold(uint256,uint256)` and selector `0xe0303301` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct LagOverEscapeHatchThresholdReturn(pub bool); ///Container type for all return fields from the `owner` function with signature `owner()` and selector `0x8da5cb5b` #[derive( Clone, @@ -3371,6 +4073,20 @@ pub mod light_client_mock { Hash, )] pub struct ProxiableUUIDReturn(pub [u8; 32]); + ///Container type for all return fields from the `stateUpdateBlockNumbers` function with signature `stateUpdateBlockNumbers(uint256)` and selector `0xa51e6fea` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + pub struct StateUpdateBlockNumbersReturn(pub ::ethers::core::types::U256); ///Container type for all return fields from the `states` function with signature `states(uint32)` and selector `0x7f17baad` #[derive( Clone, diff --git a/contract-bindings/src/shared_types.rs b/contract-bindings/src/shared_types.rs index 18211d22a..0176246d0 100644 --- a/contract-bindings/src/shared_types.rs +++ b/contract-bindings/src/shared_types.rs @@ -53,6 +53,23 @@ pub struct PlonkProof { pub sigma_eval_3: ::ethers::core::types::U256, pub prod_perm_zeta_omega_eval: ::ethers::core::types::U256, } +///`HotShotCommitment(uint64,uint256)` +#[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, +)] +pub struct HotShotCommitment { + pub block_height: u64, + pub block_comm_root: ::ethers::core::types::U256, +} ///`LightClientState(uint64,uint64,uint256,uint256,uint256,uint256,uint256,uint256)` #[derive( Clone, diff --git a/contracts/.gitignore b/contracts/.gitignore index 5c4627912..b68c24227 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -13,3 +13,10 @@ out/ # Dotenv file .env + +.wake +pytypes +__pycache__/ +*.py[cod] +.hypothesis/ +wake-coverage.cov \ No newline at end of file diff --git a/contracts/rust/adapter/Cargo.toml b/contracts/rust/adapter/Cargo.toml index 3072bfc8a..d242940fc 100644 --- a/contracts/rust/adapter/Cargo.toml +++ b/contracts/rust/adapter/Cargo.toml @@ -18,8 +18,8 @@ contract-bindings = { path = "../../../contract-bindings" } diff-test-bn254 = { git = "https://github.com/EspressoSystems/solidity-bn254.git" } ethers = { version = "2.0.4" } hotshot-types = { workspace = true } +jf-pcs = { workspace = true } jf-plonk = { workspace = true } -jf-primitives = { workspace = true } jf-utils = { workspace = true } num-bigint = { version = "0.4", default-features = false } num-traits = { version = "0.2", default-features = false } diff --git a/contracts/rust/adapter/src/jellyfish.rs b/contracts/rust/adapter/src/jellyfish.rs index 18943d847..1df5a92ab 100644 --- a/contracts/rust/adapter/src/jellyfish.rs +++ b/contracts/rust/adapter/src/jellyfish.rs @@ -11,10 +11,10 @@ use ethers::{ prelude::{AbiError, EthAbiCodec, EthAbiType}, types::{Bytes, H256, U256}, }; +use jf_pcs::prelude::Commitment; use jf_plonk::proof_system::structs::{OpenKey, Proof, ProofEvaluations, VerifyingKey}; use jf_plonk::testing_apis::Challenges; use jf_plonk::{constants::KECCAK256_STATE_SIZE, transcript::SolidityTranscript}; -use jf_primitives::pcs::prelude::Commitment; use num_bigint::BigUint; use num_traits::Num; diff --git a/contracts/rust/diff-test/Cargo.toml b/contracts/rust/diff-test/Cargo.toml index 59fdae91a..6669efe59 100644 --- a/contracts/rust/diff-test/Cargo.toml +++ b/contracts/rust/diff-test/Cargo.toml @@ -23,9 +23,10 @@ hotshot-stake-table = { workspace = true } hotshot-state-prover = { path = "../../../hotshot-state-prover" } hotshot-types = { workspace = true } itertools = { workspace = true } +jf-pcs = { workspace = true } jf-plonk = { workspace = true } -jf-primitives = { workspace = true } jf-relation = { workspace = true } +jf-signature = { workspace = true } jf-utils = { workspace = true } sha3 = { version = "0.10.8", default-features = false } diff --git a/contracts/rust/diff-test/src/main.rs b/contracts/rust/diff-test/src/main.rs index b7dc3976d..5c7b58b40 100644 --- a/contracts/rust/diff-test/src/main.rs +++ b/contracts/rust/diff-test/src/main.rs @@ -17,17 +17,16 @@ use hotshot_state_prover::mock_ledger::{ gen_plonk_proof_for_test, MockLedger, MockSystemParam, STAKE_TABLE_CAPACITY, }; use itertools::multiunzip; +use jf_pcs::prelude::Commitment; use jf_plonk::proof_system::structs::{Proof, VerifyingKey}; use jf_plonk::proof_system::PlonkKzgSnark; use jf_plonk::{ testing_apis::Verifier, transcript::{PlonkTranscript, SolidityTranscript}, }; -use jf_primitives::constants::CS_ID_BLS_BN254; -use jf_primitives::pcs::prelude::Commitment; -use jf_primitives::signatures::bls_over_bn254::Signature; -use jf_primitives::signatures::bls_over_bn254::{hash_to_curve, KeyPair as BLSKeyPair}; -use jf_primitives::signatures::schnorr::KeyPair as SchnorrKeyPair; +use jf_signature::bls_over_bn254::{hash_to_curve, KeyPair as BLSKeyPair, Signature}; +use jf_signature::constants::CS_ID_BLS_BN254; +use jf_signature::schnorr::KeyPair as SchnorrKeyPair; use sha3::Keccak256; #[derive(Parser)] diff --git a/contracts/rust/gen-vk-contract/Cargo.toml b/contracts/rust/gen-vk-contract/Cargo.toml index 595c163d0..dc4920fc7 100644 --- a/contracts/rust/gen-vk-contract/Cargo.toml +++ b/contracts/rust/gen-vk-contract/Cargo.toml @@ -10,4 +10,4 @@ ark-srs = { workspace = true } hotshot-contract-adapter = { path = "../adapter" } hotshot-stake-table = { workspace = true } hotshot-state-prover = { path = "../../../hotshot-state-prover" } -jf-primitives = { workspace = true } +jf-pcs = { workspace = true } diff --git a/contracts/rust/gen-vk-contract/src/main.rs b/contracts/rust/gen-vk-contract/src/main.rs index ce32050aa..d6804eca7 100644 --- a/contracts/rust/gen-vk-contract/src/main.rs +++ b/contracts/rust/gen-vk-contract/src/main.rs @@ -9,7 +9,7 @@ use std::process::Command; use hotshot_contract_adapter::jellyfish::ParsedVerifyingKey; use hotshot_stake_table::config::STAKE_TABLE_CAPACITY; -use jf_primitives::pcs::prelude::UnivariateUniversalParams; +use jf_pcs::prelude::UnivariateUniversalParams; fn main() { let srs = { diff --git a/contracts/script/README.md b/contracts/script/README.md index 573f613b2..6ddf07f41 100644 --- a/contracts/script/README.md +++ b/contracts/script/README.md @@ -18,10 +18,19 @@ settings. 1. Create a multisig wallet using [Safe](https://app.safe.global/welcome/accounts) on the network you'd like to deploy to. -2. In OpenZeppelin Defender, create an Approval Process that requires the multisig wallet you created above. - `Manage > Approval Processes` -3. In OpenZeppelin Defender, create a deployment environment and use the approval process created in Step 2. Be sure to - copy the Defender secret and key, that is shown at the end of this step, into the .env file. +2. In [OpenZeppelin Defender](https://www.openzeppelin.com/defender), create an Approval Process that requires the + multisig wallet you created above. `Manage > Approval Processes`. + 1. Enter a name for your approval process + 1. Enter the multisig address from shown in te Safe UI + 1. Enter one of multisig owner address addresses + 1. Save the changes +3. In OpenZeppelin Defender, create a deployment environment by clicking on "Setup" in the + [deploy](https://defender.openzeppelin.com/v2/#/deploy) tab. Use "Test Environment" for deploying to testnets (e. g. + Sepolia) and "Production Environment" for mainnet. + 1. Choose a network + 1. Select the approval process created in Step 2 + 1. Be sure to save DEFENDER_SECRET ("Team Secret key") and DEFENDER_KEY ("Team API Key"), that is shown at the end of + this step, into the .env file. The keys won't be available later at a later point. ## Deployments @@ -29,15 +38,23 @@ settings. Steps: -1. Run the Deployment Command This command requires you to go to OpenZeppelin Defender's UI to see the transaction. - Click that transaction which opens up the Safe UI where your signers for that Safe multi-sig wallet can confirm the - transaction. The two transactions to be confirmed are: (i) deployment of implementation contract (ii) deployment of - proxy contract +1. Run the Deployment command. -```bash -forge clean && \ -forge script contracts/script/FeeContractWithDefender.s.sol:FeeContractDefenderDeployScript --ffi --rpc-url https://ethereum-sepolia.publicnode.com --build-info true -``` + ```bash + forge clean && forge script contracts/script/FeeContractWithDefender.s.sol:FeeContractDefenderDeployScript --ffi --rpc-url https://ethereum-sepolia.publicnode.com --build-info true + ``` + + 1. Go to the [deploy](https://defender.openzeppelin.com/v2/#/deploy) tab OpenZeppelin Defender's UI and click on the + current environment to see the transaction. The transaction should be visible with status "SUBMITTED". The page + may need to be refreshed a few times. It occasionally may take minutes for transactions to appear. + 2. Click that transaction, then "Open in Safe App" which opens up the Safe UI where your signers for that Safe + multi-sig wallet can confirm the transaction. The two transactions to be confirmed are: (i) deployment of + implementation contract (ii) deployment of proxy contract + 3. If the transaction looks correct click "confirm". + 4. Click "Execute". + 5. Confirm the transaction with your wallet (e. g. metamask). + 6. Repeat steps 1 to 5 for the deployment of the proxy contract. You may need to refresh the OpenZeppelin Defender + "deploy" tab a few times until the second transaction appears. 2. Verify the Implementation contract on Etherscan (Use another window as step would not have completed yet) @@ -73,10 +90,11 @@ Read Deploying the Fee Contract for a more detailed version of this. 1. Initiate the Deployment with OpenZeppelin Defender ```bash -forge clean && \ -forge script contracts/script/LightClientWithDefender.s.sol:LightClientDefenderDeployScript --ffi --rpc-url https://ethereum-sepolia.publicnode.com --build-info true +forge clean && forge script contracts/script/LightClientWithDefender.s.sol:LightClientDefenderDeployScript --ffi --rpc-url https://ethereum-sepolia.publicnode.com --build-info true ``` +Follow the same steps as for the deployment of the fee contract above. + 2. Verify the Contract ```bash diff --git a/contracts/script/multisigTransactionProposals/README.md b/contracts/script/multisigTransactionProposals/README.md new file mode 100644 index 000000000..080873ee3 --- /dev/null +++ b/contracts/script/multisigTransactionProposals/README.md @@ -0,0 +1,92 @@ +# Proposing Multisig Transactions via the Safe SDK + +The [Safe SDK](https://github.com/safe-global/safe-core-sdk/blob/main/guides/integrating-the-safe-core-sdk.md) is being +used to propose transactions that only the Safe multisig admin wallet can perform. The proposer of these transactions is +also part of the multisig wallet but is used to orchestrate the process. E.g. If you require 3 of 5 trusted signers to +sign a transaction, then the multisig wallet should require 4 of 5 signers where the 4th signer is the orchestrator +wallet. + +## Set Permissioned Prover + +To enable the permissioned prover on the light client contract, ensure that the following environment variables are set +in the `.env` file: + +- `SEPOLIA_RPC_URL` +- `SAFE_ORCHESTRATOR_PRIVATE_KEY` +- `SAFE_MULTISIG_ADDRESS` +- `APPROVED_PROVER_ADDRESS` +- `LIGHT_CLIENT_CONTRACT_ADDRESS` + +Assuming you're in the root folder, run the following command: + +```bash +ts-node contracts/script/multisigTransactionProposals/safeSDK/setProverProposal.ts +``` + +Open the the URL shown in the console to sign the transaction in the Safe UI. + +Once successful, all signers will see a transaction request on the SAFE UI e.g. +`https://app.safe.global/transactions/queue?safe=$SAFE_MULTISIG_ADDRESS` + +Once the transaction has been signed by all signers and executed by one, you should be able to go to the light client +proxy and read the permissioned prover address on etherscan. + +## Disable Permissioned Prover + +To disable the permissioned prover on the light client contract, ensure that the following environment variables are set +in the `.env` file: + +- `SEPOLIA_RPC_URL` +- `SAFE_ORCHESTRATOR_PRIVATE_KEY` +- `SAFE_MULTISIG_ADDRESS` +- `LIGHT_CLIENT_CONTRACT_ADDRESS` + +Assuming you're in the root folder, run the following command: + +```bash +ts-node contracts/script/multisigTransactionProposals/safeSDK/disableProverProposal.ts +``` + +Once successful, all signers will see a transaction request on the SAFE UI +`https://app.safe.global/transactions/queue?safe=$SAFE_MULTISIG_ADDRESS` + +Once the transaction has been signed by all signers and executed by one, you should be able to go to the light client +proxy and read the permissioned prover address. It will be equal to the 0 ETH address (address(0)). + +## Demonstrating the setPermissionedProver workflow + +1. Follow the steps in the deployment script [readme](../../contracts/script/README.md) to set up OpenZeppelin Defender, + a Multisig Wallet and deploy the Light Client contract +2. Set the environment variables mentioned in the section, [Set Permissioned Prover](#set-permissioned-prover) +3. Run the `ts-node` command as mentioned in the section, [Set Permissioned Prover](#set-permissioned-prover) + +## Demonstrating the disablePermissionedProver workflow + +1. Follow the steps in the deployment script [readme](../../contracts/script/README.md) to set up OpenZeppelin Defender, + a Multisig Wallet and deploy the Light Client contract +2. Set the environment variables mentioned in the section, [Disable Permissioned Prover](#disable-permissioned-prover) +3. Run the `ts-node` command as mentioned in the section, [Disable Permissioned Prover](#disable-permissioned-prover) + +## Testing + +### Testing Safe Multisig Wallets + +The Safe Transaction Service requires a live network available for testing and the current service only supports mainnet +and testnets such as Sepolia. The Safe Wallet UI only works with public networks that they support and to customize it +for a private EVM network, read their [docs](https://help.safe.global/en/articles/40795-supported-networks) for more +info. It's non-trivial to set up Safe for private networks and therefore time has not been allocated to do so at this +stage. + +### Testing the utils + +Testing safeSDK/utils.ts + +```bash +yarn jest contracts/script/multisigTransactionProposals/tests/utils.test.ts +``` + +OR + +```bash +yarn jest +``` diff --git a/contracts/script/multisigTransactionProposals/safeSDK/disableProverProposal.ts b/contracts/script/multisigTransactionProposals/safeSDK/disableProverProposal.ts new file mode 100644 index 000000000..510bda686 --- /dev/null +++ b/contracts/script/multisigTransactionProposals/safeSDK/disableProverProposal.ts @@ -0,0 +1,118 @@ +import dotenv from "dotenv"; +import { ethers } from "ethers"; +import { EthersAdapter } from "@safe-global/protocol-kit"; +import SafeApiKit from "@safe-global/api-kit"; +import Safe from "@safe-global/protocol-kit"; +import { getEnvVar, createSafeTransactionData, isValidEthereumAddress } from "./utils"; + +async function main() { + dotenv.config(); + + try { + /**TODO + * change from SEPOLIA_RPC_URL to production URL when deploying to production + */ + // Initialize web3 provider using the RPC URL from environment variables + const web3Provider = new ethers.JsonRpcProvider(getEnvVar("SEPOLIA_RPC_URL")); + // Create a signer using the orchestrator's private key and the web3 provider + const orchestratorSigner = new ethers.Wallet(getEnvVar("SAFE_ORCHESTRATOR_PRIVATE_KEY"), web3Provider); + + // Set up Eth Adapter with ethers and the signer + const ethAdapter = new EthersAdapter({ + ethers, + signerOrProvider: orchestratorSigner, + }); + + const chainId = await ethAdapter.getChainId(); + const safeService = new SafeApiKit({ chainId }); + + const safeAddress = getEnvVar("SAFE_MULTISIG_ADDRESS"); + isValidEthereumAddress(safeAddress); + const safeSdk = await Safe.create({ ethAdapter, safeAddress }); + + await proposeDisableProverTransaction(safeSdk, safeService, await orchestratorSigner.getAddress(), safeAddress); + + console.log( + `The other owners of the Safe Multisig wallet need to sign the transaction via the Safe UI https://app.safe.global/transactions/queue?safe=sep:${safeAddress}`, + ); + } catch (error) { + throw new Error("An error occurred: " + error); + } +} + +/** + * Function to propose the transaction data for disabling permissioned prover mode + * @param {Safe} safeSDK - An instance of the Safe SDK + * @param {SafeApiKit} safeService - An instance of the Safe Service + * @param {string} signerAddress - The address of the address signing the transaction + * @param {string} safeAddress - The address of the Safe multisig wallet + */ +export async function proposeDisableProverTransaction( + safeSDK: Safe, + safeService: SafeApiKit, + signerAddress: string, + safeAddress: string, +) { + // Prepare the transaction data to disable permissioned prover mode + let data = createDisablePermissionedProverTxData(); + + const contractAddress = getEnvVar("LIGHT_CLIENT_CONTRACT_ADDRESS"); + isValidEthereumAddress(contractAddress); + + // Create the Safe Transaction Object + const safeTransaction = await createSafeTransaction(safeSDK, contractAddress, data, "0"); + + // Get the transaction hash and sign the transaction + const safeTxHash = await safeSDK.getTransactionHash(safeTransaction); + + // Sign the transaction with orchestrator signer that was specified when we created the safeSDK + const senderSignature = await safeSDK.signHash(safeTxHash); + + // Propose the transaction which can be signed by other owners via the Safe UI + await safeService.proposeTransaction({ + safeAddress: safeAddress, + safeTransactionData: safeTransaction.data, + safeTxHash: safeTxHash, + senderAddress: signerAddress, + senderSignature: senderSignature.data, + }); +} + +/** + * Function to create the transaction data for disabling permissioned prover mode + * @returns {string} - Encoded transaction data + */ +function createDisablePermissionedProverTxData(): string { + // Define the ABI of the function to be called + const abi = ["function disablePermissionedProverMode()"]; + + // Encode the function call with the provided prover address + const data = new ethers.Interface(abi).encodeFunctionData("disablePermissionedProverMode", []); + return data; // Return the encoded transaction data +} + +/** + * Creates a Safe transaction object + * + * @param {Safe} safeSDK - An instance of the Safe SDK + * @param {string} contractAddress - The address of the contract to interact with + * @param {string} data - The data payload for the transaction + * @param {string} value - The value to be sent with the transaction + * @returns {Promise} - A promise that resolves to the Safe transaction object + */ +async function createSafeTransaction( + safeSDK: Safe, + contractAddress: string, + data: string, + value: string, +): Promise { + // Prepare the safe transaction data with the contract address, data, and value + let safeTransactionData = createSafeTransactionData(contractAddress, data, value); + + // Create the safe transaction using the Safe SDK + const safeTransaction = await safeSDK.createTransaction({ transactions: [safeTransactionData] }); + + return safeTransaction; +} + +main(); diff --git a/contracts/script/multisigTransactionProposals/safeSDK/setProverProposal.ts b/contracts/script/multisigTransactionProposals/safeSDK/setProverProposal.ts new file mode 100644 index 000000000..328876c44 --- /dev/null +++ b/contracts/script/multisigTransactionProposals/safeSDK/setProverProposal.ts @@ -0,0 +1,129 @@ +import dotenv from "dotenv"; +import { ethers } from "ethers"; +import { EthersAdapter } from "@safe-global/protocol-kit"; +import SafeApiKit from "@safe-global/api-kit"; +import Safe from "@safe-global/protocol-kit"; +import { getEnvVar, createSafeTransactionData, isValidEthereumAddress } from "./utils"; + +async function main() { + dotenv.config(); + + try { + /**TODO + * change from SEPOLIA_RPC_URL to production URL when deploying to production + */ + // Initialize web3 provider using the RPC URL from environment variables + const web3Provider = new ethers.JsonRpcProvider(getEnvVar("SEPOLIA_RPC_URL")); + // Create a signer using the orchestrator's private key and the web3 provider + const orchestratorSigner = new ethers.Wallet(getEnvVar("SAFE_ORCHESTRATOR_PRIVATE_KEY"), web3Provider); + + // Set up Eth Adapter with ethers and the signer + const ethAdapter = new EthersAdapter({ + ethers, + signerOrProvider: orchestratorSigner, + }); + + const chainId = await ethAdapter.getChainId(); + const safeService = new SafeApiKit({ chainId }); + const safeAddress = getEnvVar("SAFE_MULTISIG_ADDRESS"); + isValidEthereumAddress(safeAddress); + const safeSdk = await Safe.create({ ethAdapter, safeAddress }); + + const permissionedProverAddress = getEnvVar("APPROVED_PROVER_ADDRESS"); + isValidEthereumAddress(permissionedProverAddress); + + await proposeSetProverTransaction( + safeSdk, + safeService, + await orchestratorSigner.getAddress(), + safeAddress, + permissionedProverAddress, + ); + + console.log( + `The other owners of the Safe Multisig wallet need to sign the transaction via the Safe UI https://app.safe.global/transactions/queue?safe=sep:${safeAddress}`, + ); + } catch (error) { + throw new Error("An error occurred: " + error); + } +} + +/** + * Function to propose the transaction data for setting the permissioned prover + * @param {string} safeSDK - An instance of the Safe SDK + * @param {string} safeService - An instance of the Safe Service + * @param {string} signerAddress - The address of the address signing the transaction + * @param {string} safeAddress - The address of the Safe multisig wallet + * @param {string} proverAddress - The address of the permissioned prover + */ +export async function proposeSetProverTransaction( + safeSDK: Safe, + safeService: SafeApiKit, + signerAddress: string, + safeAddress: string, + proverAddress: string, +) { + // Prepare the transaction data to set the permissioned prover + let data = createPermissionedProverTxData(proverAddress); + + const contractAddress = getEnvVar("LIGHT_CLIENT_CONTRACT_ADDRESS"); + isValidEthereumAddress(contractAddress); + + // Create the Safe Transaction Object + const safeTransaction = await createSafeTransaction(safeSDK, contractAddress, data, "0"); + + // Get the transaction hash and sign the transaction + const safeTxHash = await safeSDK.getTransactionHash(safeTransaction); + + // Sign the transaction with orchestrator signer that was specified when we created the safeSDK + const senderSignature = await safeSDK.signHash(safeTxHash); + + // Propose the transaction which can be signed by other owners via the Safe UI + await safeService.proposeTransaction({ + safeAddress: safeAddress, + safeTransactionData: safeTransaction.data, + safeTxHash: safeTxHash, + senderAddress: signerAddress, + senderSignature: senderSignature.data, + }); +} + +/** + * Function to create the transaction data for setting the permissioned prover + * @param {string} proverAddress - The address of the permissioned prover + * @returns {string} - Encoded transaction data + */ +function createPermissionedProverTxData(proverAddress: string): string { + // Define the ABI of the function to be called + const abi = ["function setPermissionedProver(address)"]; + + // Encode the function call with the provided prover address + const data = new ethers.Interface(abi).encodeFunctionData("setPermissionedProver", [proverAddress]); + return data; // Return the encoded transaction data +} + +/** + * Creates a Safe transaction object + * + * @param {Safe} safeSDK - An instance of the Safe SDK + * @param {string} contractAddress - The address of the contract to interact with + * @param {string} data - The data payload for the transaction + * @param {string} value - The value to be sent with the transaction + * @returns {Promise} - A promise that resolves to the Safe transaction object + */ +async function createSafeTransaction( + safeSDK: Safe, + contractAddress: string, + data: string, + value: string, +): Promise { + // Prepare the safe transaction data with the contract address, data, and value + let safeTransactionData = createSafeTransactionData(contractAddress, data, value); + + // Create the safe transaction using the Safe SDK + const safeTransaction = await safeSDK.createTransaction({ transactions: [safeTransactionData] }); + + return safeTransaction; +} + +main(); diff --git a/contracts/script/multisigTransactionProposals/safeSDK/utils.ts b/contracts/script/multisigTransactionProposals/safeSDK/utils.ts new file mode 100644 index 000000000..c7fe464a5 --- /dev/null +++ b/contracts/script/multisigTransactionProposals/safeSDK/utils.ts @@ -0,0 +1,53 @@ +import { ethers } from "ethers"; // Import ethers from the ethers library + +/** + * Function to check if a given string is a valid Ethereum address + * @param {string} address - The Ethereum address to validate + * @returns {boolean} - Returns true if the address is valid, throws an error otherwise + */ +export function isValidEthereumAddress(address: string) { + if (!ethers.isAddress(address)) { + throw new Error("Invalid Ethereum address format"); // Throw an error if the address is invalid + } + return true; // Return true if the address is valid +} + +/** + * Function to get the value of an environment variable from the .env file + * @param {string} name - The name of the environment variable to retrieve + * @returns {string} - Returns the value of the environment variable + */ +export function getEnvVar(name: string): string { + const value = process.env[name]; // Retrieve the environment variable value + // Check if the environment variable is undefined or empty + if (value === undefined || value === "") { + throw new Error(`Environment variable ${name} is not set`); // Throw an error if the environment variable is not set + } + return value; // Return the value of the environment variable +} + +/** + * Function to create safe transaction data to be used with the safe SDK + * @param {string} to - The destination address for the transaction + * @param {string} data - The contract data to be sent + * @param {string} value - The value to be sent + * @returns {object} - Returns the safe transaction data object + */ +export function createSafeTransactionData(to: string, data: string, value: string) { + // Check if the destination address is specified + if (to == "") { + throw new Error("must specify destination address"); // Throw an error if the destination address is not specified + } + // Check if both data and value are empty + if (data == "" && value == "") { + throw new Error("Either the contract data or value to be sent must be specified"); // Throw an error if both data and value are empty + } + isValidEthereumAddress(to); // Validate the destination address + // Create the safe transaction data object + const safeTransactionData = { + to: to, + data: data, + value: value, + }; + return safeTransactionData; // Return the safe transaction data object +} diff --git a/contracts/script/multisigTransactionProposals/tests/utils.test.ts b/contracts/script/multisigTransactionProposals/tests/utils.test.ts new file mode 100644 index 000000000..2fa1a6688 --- /dev/null +++ b/contracts/script/multisigTransactionProposals/tests/utils.test.ts @@ -0,0 +1,87 @@ +import { createSafeTransactionData, getEnvVar, isValidEthereumAddress } from "../safeSDK/utils"; + +// Mocking process.argv +const originalArgv = process.argv; +const validETHAddress = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + +describe("environment tests", () => { + afterEach(() => { + process.argv = originalArgv; // Reset argv after each test + }); + + it("should throw an error if SEPOLIA_RPC_URL environment variable is not set", () => { + process.env.SEPOLIA_RPC_URL = ""; + expect(() => getEnvVar("SEPOLIA_RPC_URL")).toThrow(); + }); + + it("should return the rpc url if SEPOLIA_RPC_URL environment variable is set", () => { + process.env.SEPOLIA_RPC_URL = "http://rpc"; + const result = getEnvVar("SEPOLIA_RPC_URL"); + expect(result).toEqual("http://rpc"); + }); +}); + +describe("createSafeTransactionData", () => { + test("should throw an error if the destination address is not specified", () => { + expect(() => { + createSafeTransactionData("", "0x", "0"); + }).toThrow("must specify destination address"); + }); + + test("should throw an error if the address is not valid", () => { + expect(() => { + createSafeTransactionData("0x123", "0x", "0"); + }).toThrow("Invalid Ethereum address format"); + }); + + test("should throw an error if both data and value are empty", () => { + expect(() => { + createSafeTransactionData(validETHAddress, "", ""); + }).toThrow("Either the contract data or value to be sent must be specified"); + }); + + test("should return safe transaction data when valid parameters are provided", () => { + const result = createSafeTransactionData(validETHAddress, "0x", "100"); + expect(result).toEqual({ + to: validETHAddress, + data: "0x", + value: "100", + }); + }); + + test("should return safe transaction data when only data is provided", () => { + const result = createSafeTransactionData(validETHAddress, "0x", ""); + expect(result).toEqual({ + to: validETHAddress, + data: "0x", + value: "", + }); + }); + + test("should return safe transaction data when only value is provided", () => { + const result = createSafeTransactionData(validETHAddress, "", "100"); + expect(result).toEqual({ + to: validETHAddress, + data: "", + value: "100", + }); + }); +}); + +describe("isValidEthereumAddress", () => { + test("should return true for a valid Ethereum address", () => { + expect(isValidEthereumAddress(validETHAddress)).toBe(true); + }); + + test("should throw an error for an invalid Ethereum address", () => { + expect(() => { + isValidEthereumAddress("0xInvalidEthereumAddress"); + }).toThrow("Invalid Ethereum address format"); + }); + + test("should throw an error for an empty string", () => { + expect(() => { + isValidEthereumAddress(""); + }).toThrow("Invalid Ethereum address format"); + }); +}); diff --git a/contracts/script/output/defenderDeployments/LightClient.sol/11155111/12.json b/contracts/script/output/defenderDeployments/LightClient.sol/11155111/12.json new file mode 100644 index 000000000..2b7d992e6 --- /dev/null +++ b/contracts/script/output/defenderDeployments/LightClient.sol/11155111/12.json @@ -0,0 +1,7 @@ +{ + "approvalProcessId": "a0dd5bf1-9766-4a2f-94b1-2a7ceb0dc5be", + "approvalType": "Gnosis Safe", + "multisig": "0xc56fA6505d10bF322e01327e22479DE78C3Bf1cE", + "proxyAddress": "0xbC781a2BCcdac8F65EF10EA85D765CA240D1789b", + "salt": 12 +} \ No newline at end of file diff --git a/contracts/script/output/defenderDeployments/LightClient.sol/11155111/saltHistory.json b/contracts/script/output/defenderDeployments/LightClient.sol/11155111/saltHistory.json new file mode 100644 index 000000000..42cc64ab9 --- /dev/null +++ b/contracts/script/output/defenderDeployments/LightClient.sol/11155111/saltHistory.json @@ -0,0 +1,4 @@ +{ + "contractName": "LightClient.sol", + "previousSalt": 12 +} \ No newline at end of file diff --git a/contracts/src/LightClient.sol b/contracts/src/LightClient.sol index 69892005f..5b7d2a69b 100644 --- a/contracts/src/LightClient.sol +++ b/contracts/src/LightClient.sol @@ -84,6 +84,13 @@ contract LightClient is Initializable, OwnableUpgradeable, UUPSUpgradeable { /// @notice a flag that indicates when a permissioned provrer is needed bool public permissionedProverEnabled; + /// @notice an array to store the L1 Block Heights where the finalizedState was updated + uint256[] public stateUpdateBlockNumbers; + + /// @notice an array to store the HotShot Block Heights and their respective HotShot + /// commitments + HotShotCommitment[] public hotShotCommitments; + // === Data Structure === // /// @notice The finalized HotShot state (as the digest of the entire HotShot state) @@ -106,6 +113,14 @@ contract LightClient is Initializable, OwnableUpgradeable, UUPSUpgradeable { uint256 threshold; } + /// @notice Simplified HotShot commitment struct + /// @param blockHeight The block height of the latest finalized HotShot block + /// @param blockCommRoot The merkle root of historical block commitments (BN254::ScalarField) + struct HotShotCommitment { + uint64 blockHeight; + BN254.ScalarField blockCommRoot; + } + /// @notice Event that a new finalized state has been successfully verified and updated event NewState( uint64 indexed viewNum, uint64 indexed blockHeight, BN254.ScalarField blockCommRoot @@ -131,6 +146,10 @@ contract LightClient is Initializable, OwnableUpgradeable, UUPSUpgradeable { error PermissionedProverNotSet(); /// @notice If the same mode or prover is sent to the function, then no change is required error NoChangeRequired(); + /// @notice Invalid L1 Block for checking Light Client Updates, premature or in the future + error InsufficientSnapshotHistory(); + /// @notice Invalid HotShot Block for checking HotShot commitments, premature or in the future + error InvalidHotShotBlockForCommitmentCheck(); /// @notice since the constructor initializes storage on this contract we disable it /// @dev storage is on the proxy contract since it calls this contract via delegatecall @@ -181,9 +200,9 @@ contract LightClient is Initializable, OwnableUpgradeable, UUPSUpgradeable { ) { revert InvalidArgs(); } - states[genesisState] = genesis; states[finalizedState] = genesis; + currentEpoch = 0; blocksPerEpoch = numBlockPerEpoch; @@ -193,6 +212,12 @@ contract LightClient is Initializable, OwnableUpgradeable, UUPSUpgradeable { votingThreshold = genesis.threshold; frozenStakeTableCommitment = initStakeTableComm; frozenThreshold = genesis.threshold; + + //add the L1 Block to stateUpdateBlockNumbers for the genesis state + stateUpdateBlockNumbers.push(block.number); + + // add the HotShot commitment for the genesis state + hotShotCommitments.push(HotShotCommitment(genesis.blockHeight, genesis.blockCommRoot)); } // === State Modifying APIs === @@ -251,6 +276,13 @@ contract LightClient is Initializable, OwnableUpgradeable, UUPSUpgradeable { // upon successful verification, update the latest finalized state states[finalizedState] = newState; + + //add the L1 Block to stateUpdateBlockNumbers for the new finalized state + stateUpdateBlockNumbers.push(block.number); + + //add the blockheight and blockCommRoot to hotShotCommitments for the new finalized state + hotShotCommitments.push(HotShotCommitment(newState.blockHeight, newState.blockCommRoot)); + emit NewState(newState.viewNum, newState.blockHeight, newState.blockCommRoot); } @@ -331,7 +363,6 @@ contract LightClient is Initializable, OwnableUpgradeable, UUPSUpgradeable { /// @notice set the permissionedProverMode to false and set the permissionedProver to address(0) /// @dev if it was already disabled (permissioneProverMode == false), then revert with - /// NoChangeRequired function disablePermissionedProverMode() public onlyOwner { if (permissionedProverEnabled) { permissionedProver = address(0); @@ -341,4 +372,80 @@ contract LightClient is Initializable, OwnableUpgradeable, UUPSUpgradeable { revert NoChangeRequired(); } } + + /// @notice check if more than threshold blocks passed since the last state update before + /// L1 Block Number + /// @param blockNumber The L1 block number + /// @param threshold The number of blocks updates to this contract is allowed to lag behind + function lagOverEscapeHatchThreshold(uint256 blockNumber, uint256 threshold) + public + view + returns (bool) + { + uint256 updatesCount = stateUpdateBlockNumbers.length; + + // Handling Edge Cases + // Edgecase 1: The block is in the future or in the past before HotShot was live + if (blockNumber > block.number || updatesCount < 3) { + revert InsufficientSnapshotHistory(); + } + + uint256 prevBlock; + bool prevUpdateFound; + + uint256 i = updatesCount - 1; + while (!prevUpdateFound) { + if (stateUpdateBlockNumbers[i] <= blockNumber) { + prevUpdateFound = true; + prevBlock = stateUpdateBlockNumbers[i]; + } + + // We don't consider the lag time for the first two updates + if (i < 2) { + break; + } + i--; + } + + // If no snapshot is found, we don't have enough history stored to tell whether HotShot was + // down. + if (!prevUpdateFound) { + revert InsufficientSnapshotHistory(); + } + + return blockNumber - prevBlock > threshold; + } + + /// @notice get the number of L1 block updates + function getStateUpdateBlockNumbersCount() public view returns (uint256) { + return stateUpdateBlockNumbers.length; + } + + /// @notice get the HotShot commitment that represents the Merkle root containing the leaf at + /// the provided height + /// @param hotShotBlockHeight hotShotBlockHeight + function getHotShotCommitment(uint256 hotShotBlockHeight) + public + view + returns (HotShotCommitment memory) + { + uint256 commitmentsHeight = hotShotCommitments.length; + if (hotShotBlockHeight >= hotShotCommitments[commitmentsHeight - 1].blockHeight) { + revert InvalidHotShotBlockForCommitmentCheck(); + } + for (uint256 i = 0; i < commitmentsHeight; i++) { + // The first commitment greater than the provided height is the root of the tree + // that leaf at that HotShot height + if (hotShotCommitments[i].blockHeight > hotShotBlockHeight) { + return hotShotCommitments[i]; + } + } + + return hotShotCommitments[commitmentsHeight - 1]; + } + + /// @notice get the number of HotShot block commitments + function getHotShotBlockCommitmentsCount() public view returns (uint256) { + return hotShotCommitments.length; + } } diff --git a/contracts/test/LightClient.t.sol b/contracts/test/LightClient.t.sol index d9da161f9..4e074e187 100644 --- a/contracts/test/LightClient.t.sol +++ b/contracts/test/LightClient.t.sol @@ -18,6 +18,7 @@ import { BN254 } from "bn254/BN254.sol"; contract LightClientCommonTest is Test { LCMock public lc; uint32 public constant BLOCKS_PER_EPOCH_TEST = 3; + uint32 public constant DELAY_THRESHOLD = 6; LC.LightClientState public genesis; // this constant should be consistent with `hotshot_contract::light_client.rs` uint64 internal constant STAKE_TABLE_CAPACITY = 10; @@ -613,3 +614,309 @@ contract LightClient_newFinalizedState_Test is LightClientCommonTest { lc.newFinalizedState(newState, proof); } } + +contract LightClient_StateUpdatesTest is LightClientCommonTest { + LC.LightClientState internal newState; + V.PlonkProof internal newProof; + + /** + * Liveness test cases to consider + * Outside of HotShot threshold, revert + * OnlyOneUpdate - HotShot is live + * OnlyTwoUpdates - HotShot is live unless blockNumber is past the 2nd blockupdate and past the + * threshold + */ + function setUp() public { + init(); + // Assert owner is correctly set, add this to check owner state + assertEq(lc.owner(), admin, "Admin should be the owner."); + + string[] memory cmds = new string[](6); + cmds[0] = "diff-test"; + cmds[1] = "mock-consecutive-finalized-states"; + cmds[2] = vm.toString(BLOCKS_PER_EPOCH_TEST); + cmds[3] = vm.toString(STAKE_TABLE_CAPACITY / 2); + cmds[4] = vm.toString(uint64(1)); + cmds[5] = vm.toString(uint64(1)); + + bytes memory result = vm.ffi(cmds); + (LC.LightClientState[] memory states, V.PlonkProof[] memory proofs) = + abi.decode(result, (LC.LightClientState[], V.PlonkProof[])); + + newState = states[1]; + newProof = proofs[1]; + } + + function test_1lBlockUpdatesIsUpdated() public { + uint256 blockUpdatesCount = lc.getStateUpdateBlockNumbersCount(); + + // Update the state and thus the l1BlockUpdates array would be updated + vm.prank(permissionedProver); + vm.expectEmit(true, true, true, true); + emit LC.NewState(newState.viewNum, newState.blockHeight, newState.blockCommRoot); + lc.newFinalizedState(newState, newProof); + + assertEq(lc.getStateUpdateBlockNumbersCount(), blockUpdatesCount + 1); + } + + function test_hotshotIsLiveFunctionWhenNoDelayOccurred() public { + // DELAY_THRESHOLD = 6 + uint256[] memory updates = new uint256[](5); + updates[0] = 1; + updates[1] = updates[0] + DELAY_THRESHOLD / 2; // 4 + updates[2] = updates[1] + DELAY_THRESHOLD / 2; // 7 + updates[3] = updates[2] + DELAY_THRESHOLD + 5; // 18 + updates[4] = updates[3] + DELAY_THRESHOLD / 2; // 21 + lc.setStateUpdateBlockNumbers(updates); + + // set the current block to block number larger than the l1 block numbers used in this test + vm.roll(updates[4] + (DELAY_THRESHOLD * 5)); + + assertEq(lc.getStateUpdateBlockNumbersCount(), 5); + + // Reverts as it's within the first two updates which aren't valid times to check since it + // was just getting initialized + vm.expectRevert(LC.InsufficientSnapshotHistory.selector); + lc.lagOverEscapeHatchThreshold(updates[1] - 1, DELAY_THRESHOLD); + + // Hotshot should be live (l1BlockNumber = 7) + assertFalse(lc.lagOverEscapeHatchThreshold(updates[2], DELAY_THRESHOLD)); + } + + function test_hotshotIsDownWhenADelayExists() public { + // DELAY_THRESHOLD = 6 + uint256[] memory updates = new uint256[](5); + updates[0] = 1; + updates[1] = updates[0] + DELAY_THRESHOLD / 2; // 4 + updates[2] = updates[1] + DELAY_THRESHOLD / 2; // 7 + updates[3] = updates[2] + DELAY_THRESHOLD + 5; // 18 + updates[4] = updates[3] + DELAY_THRESHOLD / 2; // 21 + lc.setStateUpdateBlockNumbers(updates); + + // set the current block to block number larger than the l1 block numbers used in this test + vm.roll(updates[4] + (DELAY_THRESHOLD * 5)); + + // Hotshot should be down (l1BlockNumber = 15) + // for a block that should have been recorded but wasn't due to a delay + assertTrue( + lc.lagOverEscapeHatchThreshold(updates[2] + DELAY_THRESHOLD + 2, DELAY_THRESHOLD) + ); + } + + function test_revertWhenThereAreOnlyTwoUpdates() public { + uint256[] memory updates = new uint256[](2); + updates[0] = 1; + updates[1] = updates[0] + DELAY_THRESHOLD + 5; //12 + lc.setStateUpdateBlockNumbers(updates); + + vm.roll(DELAY_THRESHOLD * 5); + + assertEq(lc.getStateUpdateBlockNumbersCount(), 2); + + vm.expectRevert(LC.InsufficientSnapshotHistory.selector); + lc.lagOverEscapeHatchThreshold(updates[0] + 2, DELAY_THRESHOLD); //3 + } + + function test_revertWhenThereIsOnlyOneUpdate() public { + uint256[] memory updates = new uint256[](1); + updates[0] = 1; + lc.setStateUpdateBlockNumbers(updates); + + vm.roll(DELAY_THRESHOLD * 3); + + assertEq(lc.getStateUpdateBlockNumbersCount(), 1); + + vm.expectRevert(LC.InsufficientSnapshotHistory.selector); + lc.lagOverEscapeHatchThreshold(updates[0] + 2, DELAY_THRESHOLD); //3 + } + + function test_revertWhenBlockRequestedWithinFirstTwoUpdates() public { + // DELAY_THRESHOLD = 6 + uint256[] memory updates = new uint256[](3); + updates[0] = 1; + updates[1] = updates[0] + DELAY_THRESHOLD / 2; // 4 + updates[2] = updates[1] + DELAY_THRESHOLD / 2; // 21 + lc.setStateUpdateBlockNumbers(updates); + + vm.roll(DELAY_THRESHOLD * 5); + + assertEq(lc.getStateUpdateBlockNumbersCount(), 3); + + vm.expectRevert(LC.InsufficientSnapshotHistory.selector); + lc.lagOverEscapeHatchThreshold(updates[0] + 2, DELAY_THRESHOLD); //3 + } + + function test_hotShotIsDownWhenBlockIsHigherThanLastRecordedAndTheDelayThresholdHasPassed() + public + { + // DELAY_THRESHOLD = 6 + uint256[] memory updates = new uint256[](3); + updates[0] = 1; + updates[1] = updates[0] + DELAY_THRESHOLD / 2; // 4 + updates[2] = updates[1] + DELAY_THRESHOLD / 2; // 21 + lc.setStateUpdateBlockNumbers(updates); + + // set the current block to block number larger than the l1 block numbers used in this test + vm.roll(updates[2] + (DELAY_THRESHOLD * 5)); + + // Hotshot should be down (l1BlockNumber = 29) + // in a block that's higher than the last recorded and past the delay threshold + assertTrue( + lc.lagOverEscapeHatchThreshold(updates[2] + DELAY_THRESHOLD + 3, DELAY_THRESHOLD) + ); + } + + function test_hotShotIsLiveWhenBlockIsHigherThanLastRecordedAndTheDelayThresholdHasNotPassed() + public + { + // DELAY_THRESHOLD = 6 + uint256[] memory updates = new uint256[](3); + updates[0] = 1; + updates[1] = updates[0] + DELAY_THRESHOLD / 2; // 4 + updates[2] = updates[1] + DELAY_THRESHOLD / 2; // 21 + lc.setStateUpdateBlockNumbers(updates); + + // set the current block to block number larger than the l1 block numbers used in this test + vm.roll(updates[2] + (DELAY_THRESHOLD * 5)); + + // Hotshot should be live (l1BlockNumber = 24) + assertFalse(lc.lagOverEscapeHatchThreshold(updates[2] + 3, DELAY_THRESHOLD)); + } + + function test_revertWhenBlockInFuture() public { + // DELAY_THRESHOLD = 6 + uint256[] memory updates = new uint256[](2); + updates[0] = 1; + updates[1] = updates[0] + DELAY_THRESHOLD / 2; // 4 + lc.setStateUpdateBlockNumbers(updates); + + // set the current block + uint256 currBlock = 20; + vm.roll(currBlock); + + vm.expectRevert(LC.InsufficientSnapshotHistory.selector); + + lc.lagOverEscapeHatchThreshold(currBlock + 5, DELAY_THRESHOLD); + } + + function test_revertWhenRequestedBlockIsBeforeHotShotFirstBlock() public { + // DELAY_THRESHOLD = 6 + uint256[] memory updates = new uint256[](2); + updates[0] = 1; + updates[1] = updates[0] + DELAY_THRESHOLD / 2; // 4 + lc.setStateUpdateBlockNumbers(updates); + + // set the current block + uint256 currBlock = 20; + vm.roll(currBlock); + + vm.expectRevert(LC.InsufficientSnapshotHistory.selector); + + lc.lagOverEscapeHatchThreshold(updates[0] - 1, DELAY_THRESHOLD); + } +} + +contract LightClient_HotShotCommUpdatesTest is LightClientCommonTest { + LC.LightClientState internal newState; + V.PlonkProof internal newProof; + + /** + * Liveness test cases to consider + * Outside of HotShot threshold, revert + * OnlyOneUpdate - HotShot is live + * OnlyTwoUpdates - HotShot is live unless blockNumber is past the 2nd blockupdate and past the + * threshold + */ + function setUp() public { + init(); + // Assert owner is correctly set, add this to check owner state + assertEq(lc.owner(), admin, "Admin should be the owner."); + + string[] memory cmds = new string[](6); + cmds[0] = "diff-test"; + cmds[1] = "mock-consecutive-finalized-states"; + cmds[2] = vm.toString(BLOCKS_PER_EPOCH_TEST); + cmds[3] = vm.toString(STAKE_TABLE_CAPACITY / 2); + cmds[4] = vm.toString(uint64(1)); + cmds[5] = vm.toString(uint64(1)); + + bytes memory result = vm.ffi(cmds); + (LC.LightClientState[] memory _states, V.PlonkProof[] memory _proofs) = + abi.decode(result, (LC.LightClientState[], V.PlonkProof[])); + + newState = _states[1]; + newProof = _proofs[1]; + } + + function assertEqBN254(BN254.ScalarField a, BN254.ScalarField b) public pure { + assertEq(BN254.ScalarField.unwrap(a), BN254.ScalarField.unwrap(b)); + } + + function assertNotEqBN254(BN254.ScalarField a, BN254.ScalarField b) public pure { + assertNotEq(BN254.ScalarField.unwrap(a), BN254.ScalarField.unwrap(b)); + } + + function test_hotShotBlockCommIsUpdated() public { + uint256 blockCommCount = lc.getHotShotBlockCommitmentsCount(); + + // Update the state and thus the l1BlockUpdates array would be updated + vm.prank(permissionedProver); + vm.expectEmit(true, true, true, true); + emit LC.NewState(newState.viewNum, newState.blockHeight, newState.blockCommRoot); + lc.newFinalizedState(newState, newProof); + + assertEq(lc.getHotShotBlockCommitmentsCount(), blockCommCount + 1); + } + + function test_hotShotBlockCommIsUpdatedXTimes() public { + uint256 blockCommCount = lc.getHotShotBlockCommitmentsCount(); + + string[] memory cmds = new string[](6); + cmds[0] = "diff-test"; + cmds[1] = "mock-consecutive-finalized-states"; + cmds[2] = vm.toString(BLOCKS_PER_EPOCH_TEST); + cmds[3] = vm.toString(STAKE_TABLE_CAPACITY / 2); + cmds[4] = vm.toString(uint64(1)); + cmds[5] = vm.toString(uint64(1)); + + bytes memory result = vm.ffi(cmds); + (LC.LightClientState[] memory _states, V.PlonkProof[] memory _proofs) = + abi.decode(result, (LC.LightClientState[], V.PlonkProof[])); + + uint256 statesCount = _states.length - 1; + // Update the state and thus the l1BlockUpdates array would be updated + for (uint8 i = 1; i <= statesCount; i++) { + LC.LightClientState memory state = _states[i]; + V.PlonkProof memory proof = _proofs[i]; + vm.prank(permissionedProver); + vm.expectEmit(true, true, true, true); + emit LC.NewState(state.viewNum, state.blockHeight, state.blockCommRoot); + lc.newFinalizedState(state, proof); + } + + assertEq(lc.getHotShotBlockCommitmentsCount(), blockCommCount + statesCount); + } + + function test_GetHotShotCommitmentValid() public { + vm.prank(permissionedProver); + vm.expectEmit(true, true, true, true); + emit LC.NewState(newState.viewNum, newState.blockHeight, newState.blockCommRoot); + lc.newFinalizedState(newState, newProof); + + // Test for a smaller hotShotBlockHeight + BN254.ScalarField blockComm = + lc.getHotShotCommitment(newState.blockHeight - 1).blockCommRoot; + assertEqBN254(blockComm, newState.blockCommRoot); + } + + function test_revertWhenGetHotShotCommitmentInvalidHigh() public { + // Get the highest HotShot blockheight recorded + uint256 numCommitments = lc.getHotShotBlockCommitmentsCount(); + (uint64 blockHeight,) = lc.hotShotCommitments(numCommitments - 1); + + // Expect revert when attempting to retrieve a block height higher than the highest one + // recorded + vm.expectRevert(LC.InvalidHotShotBlockForCommitmentCheck.selector); + lc.getHotShotCommitment(blockHeight + 1); + } +} diff --git a/contracts/test/StakeTable.t.sol b/contracts/test/StakeTable.t.sol index 3018b7fb5..d91daaee4 100644 --- a/contracts/test/StakeTable.t.sol +++ b/contracts/test/StakeTable.t.sol @@ -120,6 +120,7 @@ contract StakeTable_Test is StakeTableCommonTest { abi.decode(result, (LC.LightClientState, bytes32, bytes32)); genesis = state; + lc = new LCTest(genesis, BLOCKS_PER_EPOCH_TEST); stakeTable = new S(address(token), address(lc), 10); } diff --git a/contracts/test/mocks/LightClientMock.sol b/contracts/test/mocks/LightClientMock.sol index 7fddae0ca..06f834886 100644 --- a/contracts/test/mocks/LightClientMock.sol +++ b/contracts/test/mocks/LightClientMock.sol @@ -47,4 +47,22 @@ contract LightClientMock is LC { revert InvalidProof(); } } + + function setStateUpdateBlockNumbers(uint256[] memory values) public { + // Empty the array + delete stateUpdateBlockNumbers; + + // Set the stateUpdateBlockNumbers to the new values + stateUpdateBlockNumbers = values; + } + + function setHotShotCommitments(HotShotCommitment[] memory values) public { + // Empty the array + delete hotShotCommitments; + + // Set the hotShotCommitments to the new values + for (uint256 i = 0; i < values.length; i++) { + hotShotCommitments.push(values[i]); + } + } } diff --git a/cross-shell.nix b/cross-shell.nix index b9b3949d9..604dfd82c 100644 --- a/cross-shell.nix +++ b/cross-shell.nix @@ -1,7 +1,7 @@ # A simplest nix shell file with the project dependencies and # a cross-compilation support. -{ pkgs, RUSTFLAGS, RUST_LOG, RUST_BACKTRACE, CARGO_TARGET_DIR }: -pkgs.mkShell { +{ pkgs, envVars }: +pkgs.mkShell (envVars // { # Native project dependencies like build utilities and additional routines # like container building, linters, etc. nativeBuildInputs = with pkgs.pkgsBuildHost; [ @@ -25,5 +25,4 @@ pkgs.mkShell { rustCrossHook ]; - inherit RUSTFLAGS RUST_LOG RUST_BACKTRACE CARGO_TARGET_DIR; -} +}) diff --git a/data/README.md b/data/README.md index c4f6a6934..42d9a011d 100644 --- a/data/README.md +++ b/data/README.md @@ -1,15 +1,16 @@ # Reference Data This directory contains reference instantiations of the data types used by the sequencer which have a stable -language-agnostic interface for serialization (via JSON) and cryptographic commitments. The objects in this directory -have well-known commitments. They serve as examples of the data formats used by the Espresso Sequencer, and can be used -as test cases for ports of the serialization and commitment algorithms to other languages. +language-agnostic interface for serialization (in both `.json` files and binary `.bin` files) and cryptographic +commitments. The objects in this directory have well-known commitments. They serve as examples of the data formats used +by the Espresso Sequencer, and can be used as test cases for ports of the serialization and commitment algorithms to +other languages. -The Rust module `sequencer::block::reference` contains test cases which are designed to fail if the serialization format +The Rust module `sequencer::reference_tests` contains test cases which are designed to fail if the serialization format or commitment scheme for any of these data types changes. If you make a breaking change, you may need to update these reference objects as well. Running those tests will also print out information about the commitments of these reference objects, which can be useful for generating test cases for ports. To run them and get the output, use ```bash -cargo test --all-features -p sequencer -- --nocapture --test-threads 1 block::reference +cargo test --all-features -p sequencer -- --nocapture --test-threads 1 reference_tests ``` diff --git a/data/chain_config.bin b/data/chain_config.bin new file mode 100644 index 000000000..188812406 Binary files /dev/null and b/data/chain_config.bin differ diff --git a/data/chain_config.json b/data/chain_config.json new file mode 100644 index 000000000..4f0650f18 --- /dev/null +++ b/data/chain_config.json @@ -0,0 +1,7 @@ +{ + "base_fee": "0", + "chain_id": "35353", + "fee_contract": "0x0000000000000000000000000000000000000000", + "fee_recipient": "0x0000000000000000000000000000000000000000", + "max_block_size": "10240" +} diff --git a/data/fee_info.bin b/data/fee_info.bin new file mode 100644 index 000000000..debddd9d0 Binary files /dev/null and b/data/fee_info.bin differ diff --git a/data/fee_info.json b/data/fee_info.json new file mode 100644 index 000000000..ae0867bb1 --- /dev/null +++ b/data/fee_info.json @@ -0,0 +1,4 @@ +{ + "account": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "amount": "0" +} diff --git a/data/genesis/cappuccino.toml b/data/genesis/cappuccino.toml new file mode 100644 index 000000000..4f79e5024 --- /dev/null +++ b/data/genesis/cappuccino.toml @@ -0,0 +1,11 @@ +[stake_table] +capacity = 200 + +[chain_config] +chain_id = 0 +base_fee = '0 wei' +max_block_size = '30mb' +fee_recipient = '0x0000000000000000000000000000000000000000' + +[header] +timestamp = "1970-01-01T00:00:00Z" diff --git a/data/genesis/demo.toml b/data/genesis/demo.toml new file mode 100644 index 000000000..5c52dc386 --- /dev/null +++ b/data/genesis/demo.toml @@ -0,0 +1,25 @@ +[stake_table] +capacity = 10 + +[chain_config] +chain_id = 999999999 +base_fee = '1 wei' +max_block_size = '1mb' +fee_recipient = '0x0000000000000000000000000000000000000000' +fee_contract = '0xa15bb66138824a1c7167f5e85b957d04dd34e468' + +[header] +timestamp = "1970-01-01T00:00:00Z" + +[[upgrade]] +version = "0.2" +view = 5 +propose_window = 10 + + +[upgrade.chain_config] +chain_id = 999999999 +base_fee = '2 wei' +max_block_size = '1mb' +fee_recipient = '0x0000000000000000000000000000000000000000' +fee_contract = '0xa15bb66138824a1c7167f5e85b957d04dd34e468' diff --git a/data/genesis/staging.toml b/data/genesis/staging.toml new file mode 100644 index 000000000..02a2def78 --- /dev/null +++ b/data/genesis/staging.toml @@ -0,0 +1,11 @@ +[stake_table] +capacity = 200 + +[chain_config] +chain_id = 888888888 +base_fee = '0 wei' +max_block_size = '30mb' +fee_recipient = '0x0000000000000000000000000000000000000000' + +[header] +timestamp = "1970-01-01T00:00:00Z" diff --git a/data/header.bin b/data/header.bin new file mode 100644 index 000000000..c1fa7a2dd Binary files /dev/null and b/data/header.bin differ diff --git a/data/header.json b/data/header.json index 42769ec1d..8868bc435 100644 --- a/data/header.json +++ b/data/header.json @@ -1,19 +1,37 @@ { - "height": 42, - "timestamp": 789, - "l1_head": 124, - "l1_finalized": { - "number": 123, - "timestamp": "0x456", - "hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" - }, - "payload_commitment": "HASH~1yS-KEtL3oDZDBJdsW51Pd7zywIiHesBZsTbpOzrxOfu", - "builder_commitment": "BUILDER_COMMITMENT~1yS-KEtL3oDZDBJdsW51Pd7zywIiHesBZsTbpOzrxOdZ", - "ns_table": { - "bytes": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - }, - "block_merkle_tree_root": "MERKLE_COMM~yB4_Aqa35_PoskgTpcCR1oVLh6BUdLHIs7erHKWi-usUAAAAAAAAAAEAAAAAAAAAJg", - "fee_merkle_tree_root": "MERKLE_COMM~VJ9z239aP9GZDrHp3VxwPd_0l28Hc5KEAB1pFeCIxhYgAAAAAAAAAAIAAAAAAAAAdA", - "fee_info": { "account": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", "amount": "0x0" }, - "chain_config": { "chain_config": { "Left": { "chain_id": "0x8a19", "max_block_size": 10240, "base_fee": "0x0" } } } -} + "block_merkle_tree_root": "MERKLE_COMM~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAQA", + "builder_commitment": "BUILDER_COMMITMENT~upd4eFij7NNHTMYiVQoL-hDBw3bJiOMmCoNJxxgGwJ6h", + "builder_signature": { + "r": "0xc7b7c4062ada11d09530d759af4f2d9d84dd2d1ca6117299dee9b36e039642b6", + "s": "0x10cc3c7c0d0d69ba19348c720ee95cbb48470b9181cc32c9922af86b83fba9a2", + "v": 27 + }, + "chain_config": { + "chain_config": { + "Left": { + "base_fee": "0", + "chain_id": "35353", + "fee_contract": "0x0000000000000000000000000000000000000000", + "fee_recipient": "0x0000000000000000000000000000000000000000", + "max_block_size": "10240" + } + } + }, + "fee_info": { + "account": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "amount": "0" + }, + "fee_merkle_tree_root": "MERKLE_COMM~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAKA", + "height": 42, + "l1_finalized": { + "hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "number": 123, + "timestamp": "0x456" + }, + "l1_head": 124, + "ns_table": { + "bytes": "AQAAAO7/wAAIBAAA" + }, + "payload_commitment": "HASH~Ojg3e_G9UzTwOygEDDcbvgakaVCqMByIiFrlkLXT72qf", + "timestamp": 789 +} \ No newline at end of file diff --git a/data/l1_block.bin b/data/l1_block.bin new file mode 100644 index 000000000..ecffa9fdc Binary files /dev/null and b/data/l1_block.bin differ diff --git a/data/messages.bin b/data/messages.bin new file mode 100644 index 000000000..a3962ad3d Binary files /dev/null and b/data/messages.bin differ diff --git a/data/messages.json b/data/messages.json new file mode 100644 index 000000000..3a5a37686 --- /dev/null +++ b/data/messages.json @@ -0,0 +1,427 @@ +[ + { + "kind": { + "Consensus": { + "General": { + "Proposal": { + "_pd": null, + "data": { + "block_header": { + "block_merkle_tree_root": "MERKLE_COMM~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAQA", + "builder_commitment": "BUILDER_COMMITMENT~tEvs0rxqOiMCvfe2R0omNNaphSlUiEDrb2q0IZpRcgA_", + "builder_signature": null, + "chain_config": { + "chain_config": { + "Left": { + "base_fee": "0", + "chain_id": "35353", + "fee_contract": null, + "fee_recipient": "0x0000000000000000000000000000000000000000", + "max_block_size": "10240" + } + } + }, + "fee_info": { + "account": "0x0000000000000000000000000000000000000000", + "amount": "0" + }, + "fee_merkle_tree_root": "MERKLE_COMM~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAKA", + "height": 0, + "l1_finalized": null, + "l1_head": 0, + "ns_table": { + "bytes": "AAAAAA==" + }, + "payload_commitment": "HASH~AazstQer_ho1SqgGT0r10_Gs0BnjfbPBHJdSO3HHbp29", + "timestamp": 0 + }, + "justify_qc": { + "_pd": null, + "data": { + "leaf_commit": "COMMIT~eaBGKF8-lw-t211wxLq7tcXEEqacbvDVcFFR9aCddO3G" + }, + "signatures": null, + "view_number": 0, + "vote_commitment": "COMMIT~0-ZxNgMSsUEPeGDdq5-TZE8PDsYwgI4O2fVGMKooP87D" + }, + "proposal_certificate": { + "Timeout": { + "_pd": null, + "data": { + "view": 0 + }, + "signatures": null, + "view_number": 0, + "vote_commitment": "COMMIT~TZG1F34lxU6Ny9aKQMkjZAxjW9zotdwW75EHEGbyALOi" + } + }, + "upgrade_certificate": { + "_pd": null, + "data": { + "decide_by": 0, + "new_version": { + "major": 1, + "minor": 0 + }, + "new_version_first_view": 0, + "new_version_hash": [], + "old_version": { + "major": 0, + "minor": 1 + }, + "old_version_last_view": 0 + }, + "signatures": null, + "view_number": 0, + "vote_commitment": "COMMIT~roiQgLLeI4uYqYxjz-0jPwtLyNhSlnfriPEVZ-_4RUrW" + }, + "view_number": 0 + }, + "signature": "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "Vote": { + "data": { + "leaf_commit": "COMMIT~eaBGKF8-lw-t211wxLq7tcXEEqacbvDVcFFR9aCddO3G" + }, + "signature": [ + "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U", + "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + ], + "view_number": 0 + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "ViewSyncPreCommitVote": { + "data": { + "relay": 0, + "round": 0 + }, + "signature": [ + "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U", + "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + ], + "view_number": 0 + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "ViewSyncCommitVote": { + "data": { + "relay": 0, + "round": 0 + }, + "signature": [ + "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U", + "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + ], + "view_number": 0 + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "ViewSyncFinalizeVote": { + "data": { + "relay": 0, + "round": 0 + }, + "signature": [ + "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U", + "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + ], + "view_number": 0 + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "ViewSyncPreCommitCertificate": { + "_pd": null, + "data": { + "relay": 0, + "round": 0 + }, + "signatures": null, + "view_number": 0, + "vote_commitment": "COMMIT~OQecZxfFpuEuPJgkpsQoglnqY0fm6Qi1PUarYCgiFQ0T" + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "ViewSyncCommitCertificate": { + "_pd": null, + "data": { + "relay": 0, + "round": 0 + }, + "signatures": null, + "view_number": 0, + "vote_commitment": "COMMIT~POgBCaDjtUV3Il5-FXVr5KN2KzYSgipfKX6Ci0-nxduO" + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "ViewSyncFinalizeCertificate": { + "_pd": null, + "data": { + "relay": 0, + "round": 0 + }, + "signatures": null, + "view_number": 0, + "vote_commitment": "COMMIT~s5i9wmQWH7VU90CUiEWRdAG19LI1iXydSMxp9gZ7kHco" + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "TimeoutVote": { + "data": { + "view": 0 + }, + "signature": [ + "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U", + "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + ], + "view_number": 0 + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "UpgradeProposal": { + "_pd": null, + "data": { + "upgrade_proposal": { + "decide_by": 0, + "new_version": { + "major": 1, + "minor": 0 + }, + "new_version_first_view": 0, + "new_version_hash": [], + "old_version": { + "major": 0, + "minor": 1 + }, + "old_version_last_view": 0 + }, + "view_number": 0 + }, + "signature": "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "General": { + "UpgradeVote": { + "data": { + "decide_by": 0, + "new_version": { + "major": 1, + "minor": 0 + }, + "new_version_first_view": 0, + "new_version_hash": [], + "old_version": { + "major": 0, + "minor": 1 + }, + "old_version_last_view": 0 + }, + "signature": [ + "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U", + "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + ], + "view_number": 0 + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "Da": { + "DaProposal": { + "_pd": null, + "data": { + "encoded_transactions": [ + 1, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 1, + 2, + 3 + ], + "metadata": { + "bytes": "AQAAAAEAAAALAAAA" + }, + "view_number": 0 + }, + "signature": "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "Da": { + "DaVote": { + "data": { + "payload_commit": "HASH~AazstQer_ho1SqgGT0r10_Gs0BnjfbPBHJdSO3HHbp29" + }, + "signature": [ + "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U", + "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + ], + "view_number": 0 + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "Da": { + "DaCertificate": { + "_pd": null, + "data": { + "payload_commit": "HASH~AazstQer_ho1SqgGT0r10_Gs0BnjfbPBHJdSO3HHbp29" + }, + "signatures": null, + "view_number": 0, + "vote_commitment": "COMMIT~5E3F3rC4E9DBhMBqOTmjZ9tjX4VFaV5gqrZJl0y6V05D" + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Consensus": { + "Da": { + "VidDisperseMsg": { + "_pd": null, + "data": { + "common": { + "all_evals_digest": "FIELD~rF4TMFZMXJCieDeov31aNuDG5nDGR-iQdteEgBjXkErn", + "multiplicity": 1, + "num_storage_nodes": 1, + "payload_byte_len": 11, + "poly_commits": "FIELD~AQAAAAAAAAD2xsICwO-z0CXx_ucl0FV1j-zJ3tgPO-OL8gYLvXkIkNE" + }, + "payload_commitment": "HASH~Z03vXeC1EEaBGf5iacsBEWYiA7PHi3K6uS9gVpmlUx3t", + "recipient_key": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U", + "share": { + "aggregate_proofs": "FIELD~AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQGY", + "evals": "FIELD~AQAAAAAAAAABAAAAAwAAAAECAwAAAAAAAAAAAAAAAAAAAAAAAAAAAMk", + "evals_proof": { + "pos": "FIELD~AAAAAAAAAAD7", + "proof": [ + { + "Leaf": { + "elem": "FIELD~AQAAAAAAAAABAAAAAwAAAAECAwAAAAAAAAAAAAAAAAAAAAAAAAAAAMk", + "pos": "FIELD~AAAAAAAAAAD7", + "value": "FIELD~rF4TMFZMXJCieDeov31aNuDG5nDGR-iQdteEgBjXkErn" + } + } + ] + }, + "index": 0 + }, + "view_number": 0 + }, + "signature": "BLS_SIG~g3CUcLMD7fnDsBhItKvSqXLwEqdWfvusSrgpL1GBAxf-SWFW0t32Agt2jrOiempjjpI7dBwYGgXv-0mvI4sGEEE" + } + } + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + }, + { + "kind": { + "Data": { + "SubmitTransaction": [ + { + "namespace": 1, + "payload": "AQID" + }, + 0 + ] + } + }, + "sender": "BLS_VER_KEY~bQszS-QKYvUij2g20VqS8asttGSb95NrTu2PUj0uMh1CBUxNy1FqyPDjZqB29M7ZbjWqj79QkEOWkpga84AmDYUeTuWmy-0P1AdKHD3ehc-dKvei78BDj5USwXPJiDUlCxvYs_9rWYhagaq-5_LXENr78xel17spftNd5MA1Mw5U" + } +] \ No newline at end of file diff --git a/data/ns_table.bin b/data/ns_table.bin new file mode 100644 index 000000000..82defd7bc Binary files /dev/null and b/data/ns_table.bin differ diff --git a/data/ns_table.json b/data/ns_table.json index 767edc5b0..6e29afa0b 100644 --- a/data/ns_table.json +++ b/data/ns_table.json @@ -1,3 +1,3 @@ { - "bytes": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" -} + "bytes": "AQAAAO7/wAAIBAAA" +} \ No newline at end of file diff --git a/data/payload.bin b/data/payload.bin new file mode 100644 index 000000000..719c44d63 Binary files /dev/null and b/data/payload.bin differ diff --git a/data/payload.json b/data/payload.json new file mode 100644 index 000000000..3ece05b31 --- /dev/null +++ b/data/payload.json @@ -0,0 +1,6 @@ +{ + "ns_table": { + "bytes": "AQAAAO7/wAAIBAAA" + }, + "raw_payload": "AQAAAAAEAAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3+Dh4uPk5ebn6Onq6+zt7u/w8fLz9PX29/j5+vv8/f4AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3+Dh4uPk5ebn6Onq6+zt7u/w8fLz9PX29/j5+vv8/f4AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3+Dh4uPk5ebn6Onq6+zt7u/w8fLz9PX29/j5+vv8/f4AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3+Dh4uPk5ebn6Onq6+zt7u/w8fLz9PX29/j5+vv8/f4AAQID" +} \ No newline at end of file diff --git a/data/transaction.bin b/data/transaction.bin new file mode 100644 index 000000000..a0e15c9e9 Binary files /dev/null and b/data/transaction.bin differ diff --git a/data/transaction.json b/data/transaction.json index 201c1350e..5ca56e3e3 100644 --- a/data/transaction.json +++ b/data/transaction.json @@ -1,4 +1,4 @@ { - "namespace": 12648430, - "payload": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gRG9uZWMgbGVjdHVzIHZlbGl0LCBjb21tb2RvIGVnZXQgdGVsbHVzIHZpdGFlLCBtb2xlc3RpZSBtYXhpbXVzIHR1cnBpcy4gTWFlY2VuYXMgbGFjdXMgbWF1cmlzLCBhdWN0b3IgcXVpcyBsYWN1cyBhdCwgYXVjdG9yIHZvbHV0cGF0IG5pc2kuIEZ1c2NlIG1vbGVzdGllIHVybmEgc2l0IGFtZXQgcXVhbSBpbXBlcmRpZXQgc3VzY2lwaXQuIERvbmVjIGVsaXQgbGVjdHVzLCBkYXBpYnVzIGluIGlwc3VtIGV0LCB2aXZlcnJhIHBoYXJldHJhIGZlbGlzLiBTZWQgc2VkIHNlbSBzZWQgbGliZXJvIHNlbXBlciBwb3N1ZXJlLiBVdCBldWlzbW9kIHB1cnVzIGF0IG1vbGVzdGllIHZvbHV0cGF0LiBOdW5jIGV1aXNtb2QgaWQgZXN0IG5lYyBldWlzbW9kLiBBbGlxdWFtIHF1aXMgZXJhdCBiaWJlbmR1bSwgZWdlc3RhcyBhdWd1ZSBxdWlzLCB0aW5jaWR1bnQgdGVsbHVzLiBEdWlzIGRhcGlidXMgYWMganVzdG8gdXQgcmhvbmN1cy4gTnVsbGEgdmVoaWN1bGEgYXVndWUgbm9uIGFyY3UgdmVzdGlidWx1bSB0ZW1wdXMuIER1aXMgdWxsYW1jb3JwZXIgc2l0IGFtZXQgbGFjdXMgZXQgZGlnbmlzc2ltLiBNYXVyaXMgYXVjdG9yIHNvbGxpY2l0dWRpbiBmZXVnaWF0LiBGdXNjZSB0aW5jaWR1bnQgY29uZGltZW50dW0gZGFwaWJ1cy4gQWxpcXVhbSBhcmN1IGxlY3R1cywgYmxhbmRpdCBzZWQgc2VtIHNpdCBhbWV0LCBmZXJtZW50dW0gdmVoaWN1bGEgbWV0dXMuIE1hZWNlbmFzIHR1cnBpcyBuZXF1ZSwgdHJpc3RpcXVlIGVnZXQgdGluY2lkdW50IHV0LCBzY2VsZXJpc3F1ZSBldSBsYWN1cy4gVXQgYmxhbmRpdCBldSBsZW8gdml0YWUgdm9sdXRwYXQu" + "namespace": 12648430, + "payload": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+AAECAw==" } diff --git a/doc/architecture.puml b/doc/architecture.puml index b51d13463..c45e7edb7 100644 --- a/doc/architecture.puml +++ b/doc/architecture.puml @@ -1,82 +1,150 @@ @startuml -component "HotShot Sequencer" as Seq -package "Layer 2s" as L2 { - component "Rollup 1" as L2_1 { - component "API (e.g. JSON-RPC)" as L2_1_RPC - component Executor as L2_1_Exec - component Prover as L2_1_Prover - component "State DB" as L2_1_State + +package "Espresso Sequencer Network" as Network #MistyRose { + component "Other\nSequencer\nNodes" as Nodes + package CDN { + component Broker + component Marshal + database KeyDB } - component "Rollup 2" as L2_2 - component "Rollup N" as L2_N -} -package "Layer 1" as L1 { - component "Sequencer Contract" as Seq_L1 - package "Rollup Contracts" as L2_L1 { - component "Rollup 1\nContract" as L2_1_L1 - component "Rollup 2\nContract" as L2_2_L1 - component "Rollup N\nContract" as L2_N_L1 + + 'CDN Layout + Broker -> KeyDB: state + KeyDB -> Marshal: state + + component "Sequencer Node" as Seq #Bisque { + database "Postgres" as DB + package APIs #LightCyan { + component "Submit" as Submit + component "Hotshot\nEvents\nStream\n" as EventsStream + component "Catchup" as Catchup + component "Status" + component "Query" as Query + } + package HotShot { + component "Networking"{ + component Libp2p + component "CDN Client" + } + component "HotShot Events" as Events + } + + package State as State { + component "State Signer" as Signer + component "ValidatedState" as Validated + } } + component "HotShot state\nprover service" as HSProver + component "HotShot state\nrelay service" as Relay + component "Builder" as Builder } -Client -up-> L2_1_RPC : Transactions\nState queries -note right of link -1. Many clients submit transactions - to each L2 simultaneously (for - clarity only one is shown) +' Network +Nodes <-up- Catchup: "missing\nstate" " " +Nodes <-> Libp2p: consensus\nmessages +"CDN Client" <--> CDN: consensus\nmessages +Nodes <--> CDN: consensus\nmessages +note top of link + 4. HotShot consensus end note -L2_1_RPC -> Seq : Transaction\nRollup 1 ID +' HotShot state +Signer --> Relay: state, signature note top of link -2. L2s forward transactions to sequencer. + 6. HotShot proving end note +Relay -> HSProver: state,\nsignature bundle -Seq -down-> L2_1_Exec : Block -Seq -down-> L2_2 : Block -Seq -down-> L2_N : Block -note right of link -3. Sequencer produces blocks -- - ordered lists of transactions. - L2s receive blocks and execute - transactions for their rollup. +' Builder +Builder --> HotShot: Espresso block +note top of link + 3. Leader + obtains + Espresso + block end note +EventsStream -> Builder: transactions -Seq -down-> Seq_L1 : Block Commitment\nQC -note right of link -4. Sequencer posts succinct commitment - to block to L1. Contract verifies proof - of sequencing (Quorum Certificate) - then stores the block commitment. +' Events +Networking <-up-> Events +Events -up-> EventsStream: events +Events -down-> Validated + +' Storage +Query --> DB +Validated -up-> DB +DB --> Catchup: missing\nstate + +package "Layer 2s / Rollups" as L2s #LightYellow { + component "zk Rollup Z" as ZK #LightBlue { + component "API (e.g. JSON-RPC)" as ZK_RPC + component Executor as ZK_Exec + component Prover as ZK_Prover + ZK_RPC --[hidden]> ZK_Exec + ZK_RPC --[hidden]> ZK_Prover + } + component "Optimistic Rollup O" #LightGreen { + component "API (e.g. JSON-RPC)" as OR_RPC + component Proposer + component Challenger + OR_RPC --[hidden]> Proposer + OR_RPC --[hidden]> Challenger + } +} +package "Ethereum Layer 1" as L1 #LightGray { + package "Espresso Contracts" as Esp_L1 { + component "Light Client\nContract" as LC_L1 + component "Fee\nContract" as Fee_L1 + } + package "Rollup Contracts" { + component "zk Rollup Z\nContract" as ZK_L1 + component "OR Rollup O\nContract" as OR_L1 + } +} + +' Transaction submission +entity User +User --> ZK_RPC : rollup transaction,\nstate queries +note top of link + 2. Transaction submission end note +User --> OR_RPC -L2_1_Prover -> L2_1_L1 : State\nProof -L2_2 -down-> L2_2_L1 : State\nProof -L2_N -down-> L2_N_L1 : State\nProof -note right of link -5. Rollups post updated state to L1. - ZK-rollups include a proof. +ZK_RPC --> Submit : "transaction" " " +Submit -> Events: transaction +Query -up-> ZK_Exec : " " "Espresso\nheader,\nnamespace\ntransactions" +note top of link + 5. Rollup + block + production end note +Query -up-> Proposer: " " "Espresso\nheader,\nnamespace\ntransactions" -Seq_L1 -> L2_L1 -note bottom of link -6. Rollup contracts read certified sequence - of block commitments from sequencer - contract. Verify state transition proofs - against this sequence (ZKR) or wait for - fraud proof against this sequence (ORU). + +' L1 details +HSProver -up-> LC_L1 : HotShot\nstate\nproof +LC_L1 -up-> ZK_L1 : " " "HotShot\ncommitment" +LC_L1 -up-> OR_L1 : " " "HotShot\ncommitment" + +' ZK proof +ZK_Prover --> ZK_L1 : rollup state proof +note top of link + 7. ZK Rollup + proving +end note + +' OR dispute +Proposer --> OR_L1: respond +Challenger --> OR_L1: challenge +note top of link + 8. OR dispute +end note + +Builder -> Fee_L1: deposit\nether +note top of link + 1. Fee + deposit end note +Fee_L1 --> Validated: fee\ndeposits -' L2 1 details -L2_1_RPC <-down- L2_1_State : Read state -L2_1_Exec -down-> L2_1_State : Write state -L2_1_Prover <-down- L2_1_State : Read State - -' Layout -L2_1_RPC -[hidden]r-> L2_1_Exec -L2_1 -[hidden]r-> L2_2 -L2_2 -[hidden]r-> L2_N -L2_1_L1 -[hidden]r-> L2_2_L1 -L2_2_L1 -[hidden]r-> L2_N_L1 -L2 -[hidden]d-> L1 -Seq -[hidden]d-> L2 @enduml diff --git a/doc/architecture.svg b/doc/architecture.svg index 309240c0c..4836bc167 100644 --- a/doc/architecture.svg +++ b/doc/architecture.svg @@ -1 +1 @@ -Layer 2sRollup 1Layer 1Rollup ContractsRollup 2Rollup NAPI (e.g. JSON-RPC)ExecutorProverState DBSequencer ContractRollup 1ContractRollup 2ContractRollup NContractHotShot SequencerClientTransactionsState queries1. Many clients submit transactionsto each L2 simultaneously (forclarity only one is shown)2. L2s forward transactions to sequencer.TransactionRollup 1 IDBlockBlockBlock3. Sequencer produces blocks --ordered lists of transactions.L2s receive blocks and executetransactions for their rollup.Block CommitmentQC4. Sequencer posts succinct commitmentto block to L1. Contract verifies proofof sequencing (Quorum Certificate)then stores the block commitment.StateProofStateProofStateProof5. Rollups post updated state to L1.ZK-rollups include a proof.6. Rollup contracts read certified sequenceof block commitments from sequencercontract. Verify state transition proofsagainst this sequence (ZKR) or wait forfraud proof against this sequence (ORU).Read stateWrite stateRead State \ No newline at end of file +Espresso Sequencer NetworkCDNSequencer NodeAPIsHotShotNetworkingStateLayer 2s / Rollupszk Rollup ZOptimistic Rollup OEthereum Layer 1Espresso ContractsRollup ContractsOtherSequencerNodesHotShot stateprover serviceHotShot staterelay serviceBuilderBrokerMarshalKeyDBPostgresSubmitHotshotEventsStream CatchupStatusQueryHotShot EventsLibp2pCDN ClientState SignerValidatedStateAPI (e.g. JSON-RPC)ExecutorProverAPI (e.g. JSON-RPC)ProposerChallengerLight ClientContractFeeContractzk Rollup ZContractOR Rollup OContractUserstatestate  missingstateconsensusmessagesconsensusmessages4. HotShot consensusconsensusmessages6. HotShot provingstate, signaturestate,signature bundle3. LeaderobtainsEspressoblockEspresso blocktransactionseventsmissingstate2. Transaction submissionrollup transaction,state queries transaction transaction5. Rollupblockproduction Espressoheader,namespacetransactions  Espressoheader,namespacetransactions HotShotstateproof HotShotcommitment  HotShotcommitment 7. ZK Rollupprovingrollup state proofrespond8. OR disputechallenge1. Feedepositdepositetherfeedeposits \ No newline at end of file diff --git a/doc/sequence-diagram.puml b/doc/sequence-diagram.puml new file mode 100644 index 000000000..1e476c7e0 --- /dev/null +++ b/doc/sequence-diagram.puml @@ -0,0 +1,97 @@ +@startuml +!pragma teoz true +actor User + +box Layer 2s / Rollups #LightYellow + box "Optimistic Rollup O" #LightGreen + participant "API\n(RPC)" as orrpc + participant Proposer + participant Challenger + end box + box "ZK Rollup Z" #LightBlue + participant "API\n(RPC)" as zkrpc + participant "Prover" as rprover + participant "Executor" as executor + end box +end box + +box "Ethereum L1" #LightGrey + participant "Rollup\nContract" as RC + participant "Light\nClient\nContract" as LC + participant "Fee\nContract" as fee +end box + +box Espresso Sequencer Network #MistyRose + box Sequencer Node #Bisque + box APIs #LightCyan + participant "Submit\nAPI" as submit + participant "Query\nAPI" as query + end box + participant "HotShot\nEvents\nStream" as events + participant "Validated\nState" as ValidatedState + participant "State\nSigner" as signer + end box + + participant Builder + participant "HotShot\nLeader" as leader + participant CDN + participant Libp2p + participant "HotShot\nreplicas" as replicas + participant "HotShot\nstate\nrelay\nservice" as relay + participant "HotShot\nstate\nprover\nservice" as prover +end box + +group 1. Fee deposit + Builder -> fee: fee deposit +end group + +group 2. Transaction submisssion + User -> orrpc: rollup O transaction + orrpc -> submit: transaction + User -> zkrpc: rollup Z transaction + zkrpc -> submit: transaction + events -> Builder: transactions +end group + +group 3. Leader obtains Espresso block + Builder -> leader: available blocks + leader -> Builder: claim block +end group + +group 4. HotShot consensus + leader <-> replicas: consensus messages + & leader <-> Libp2p: + & Libp2p <-> replicas: + & replicas -> CDN: + fee -> ValidatedState: credit fee deposit + ValidatedState -> ValidatedState: apply header\ncharge fee +end group + +group 5. Rollup block production + query -> executor: Espresso header,\ntransactions in namespace + executor -> executor: verify\nnamespace\nproof + executor -> rprover: rollup block +end group + +group 6. HotShot proving + signer -> relay: state, signature + relay -> prover: state,\nsignature bundle + prover -> "Light\nClient\nContract" as LC: proof + LC -> LC: verify +end group + +group 7. ZK Rollup proving + rprover -> RC: rollup state proof + group verify + LC -> "Rollup\nContract" as RC: HotShot\ncommitment + end group +end group + +group 8. Optimistic Rollup dispute + Challenger -> RC: challenge + loop until dispute resolved + Proposer -> RC: respond + LC --> RC: HotShot\ncommitment + Challenger -> RC: respond +end group +@enduml diff --git a/doc/sequence-diagram.svg b/doc/sequence-diagram.svg new file mode 100644 index 000000000..1b5bf3cd2 --- /dev/null +++ b/doc/sequence-diagram.svg @@ -0,0 +1 @@ +Layer 2s / RollupsOptimistic Rollup OZK Rollup ZEthereum L1Espresso Sequencer NetworkSequencer NodeAPIsUserAPI(RPC)ProposerChallengerAPI(RPC)ProverExecutorRollupContractLightClientContractFeeContractSubmitAPIQueryAPIHotShotEventsStreamValidatedStateStateSignerBuilderHotShotLeaderCDNLibp2pHotShotreplicasHotShotstaterelayserviceHotShotstateproverserviceUserAPI(RPC)ProposerChallengerAPI(RPC)ProverExecutorRollupContractLightClientContractFeeContractSubmitAPIQueryAPIHotShotEventsStreamValidatedStateStateSignerBuilderHotShotLeaderCDNLibp2pHotShotreplicasHotShotstaterelayserviceHotShotstateproverservice1. Fee depositfee deposit2. Transaction submisssionrollup O transactiontransactionrollup Z transactiontransactiontransactions3. Leader obtains Espresso blockavailable blocksclaim block4. HotShot consensusconsensus messagescredit fee depositapply headercharge fee5. Rollup block productionEspresso header,transactions in namespaceverifynamespaceproofrollup block6. HotShot provingstate, signaturestate,signature bundleproofverify7. ZK Rollup provingrollup state proofverifyHotShotcommitment8. Optimistic Rollup disputechallengeloop[until dispute resolved]respondHotShotcommitmentrespond \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index c92035744..ec1339e59 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -15,11 +15,27 @@ services: - "./geth-config/genesis-default.json:/genesis.json" - "./geth-config/test-jwt-secret.txt:/config/test-jwt-secret.txt" - deploy-contracts: + deploy-sequencer-contracts: image: ghcr.io/espressosystems/espresso-sequencer/deploy:main + command: deploy --only fee-contract + environment: + - ESPRESSO_SEQUENCER_L1_PROVIDER + - ESPRESSO_DEPLOYER_ACCOUNT_INDEX + - RUST_LOG + - RUST_LOG_FORMAT + - ASYNC_STD_THREAD_COUNT + depends_on: + demo-l1-network: + condition: service_healthy + + deploy-prover-contracts: + image: ghcr.io/espressosystems/espresso-sequencer/deploy:main + command: deploy --use-mock-contract --only hotshot,light-client environment: - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL - ESPRESSO_SEQUENCER_L1_PROVIDER + - ESPRESSO_SEQUENCER_URL + - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY - ESPRESSO_DEPLOYER_ACCOUNT_INDEX - RUST_LOG - RUST_LOG_FORMAT @@ -27,7 +43,27 @@ services: depends_on: demo-l1-network: condition: service_healthy - orchestrator: + sequencer0: + condition: service_healthy + # Make sure this doesn't start until the other contracts have been deployed, since we use the same mnemonic. + deploy-sequencer-contracts: + condition: service_completed_successfully + + fund-builder: + image: ghcr.io/espressosystems/espresso-sequencer/bridge:main + command: espresso-bridge deposit + environment: + - L1_PROVIDER=$ESPRESSO_SEQUENCER_L1_PROVIDER + - ESPRESSO_PROVIDER=http://sequencer1:$ESPRESSO_SEQUENCER_API_PORT + - CONTRACT_ADDRESS=0xa15bb66138824a1c7167f5e85b957d04dd34e468 + - MNEMONIC=$ESPRESSO_BUILDER_ETH_MNEMONIC + - ACCOUNT_INDEX=$ESPRESSO_BUILDER_ETH_ACCOUNT_INDEX + - AMOUNT=1000000000000000000 + - CONFIRMATIONS=1 + depends_on: + deploy-sequencer-contracts: + condition: service_completed_successfully + sequencer1: condition: service_healthy orchestrator: @@ -35,7 +71,7 @@ services: ports: - "$ESPRESSO_ORCHESTRATOR_PORT:$ESPRESSO_ORCHESTRATOR_PORT" environment: - - ESPRESSO_ORCHESTRATOR_BUILDER_URL=http://permissionless-builder:$ESPRESSO_BUILDER_SERVER_PORT + - ESPRESSO_ORCHESTRATOR_BUILDER_URLS=http://permissionless-builder:$ESPRESSO_BUILDER_SERVER_PORT - ESPRESSO_ORCHESTRATOR_PORT - ESPRESSO_ORCHESTRATOR_NUM_NODES - ESPRESSO_ORCHESTRATOR_START_DELAY @@ -49,7 +85,7 @@ services: # different parts of the CDN keydb: image: docker.io/eqalpha/keydb:latest - command: ["--requirepass", "changemeplease!!"] + command: [ "--requirepass", "changemeplease!!" ] healthcheck: # Attempt to PING the database test: keydb-cli --pass changemeplease!! --raw incr PING @@ -67,7 +103,7 @@ services: - cdn-marshal - -d - redis://:changemeplease!!@keydb:6379 - - -b + - -b - $ESPRESSO_CDN_SERVER_PORT - -m - 0.0.0.0:$ESPRESSO_CDN_SERVER_METRICS_PORT @@ -81,16 +117,16 @@ services: - RUST_LOG - ESPRESSO_CDN_SERVER_METRICS_PORT image: ghcr.io/espressosystems/espresso-sequencer/cdn-broker:main - command: - - cdn-broker - - -d - - redis://:changemeplease!!@keydb:6379 - - --public-advertise-endpoint - - broker-0:1738 - - --private-advertise-endpoint - - broker-0:1739 - - -m - - 0.0.0.0:$ESPRESSO_CDN_SERVER_METRICS_PORT + command: + - cdn-broker + - -d + - redis://:changemeplease!!@keydb:6379 + - --public-advertise-endpoint + - broker-0:1738 + - --private-advertise-endpoint + - broker-0:1739 + - -m + - 0.0.0.0:$ESPRESSO_CDN_SERVER_METRICS_PORT depends_on: keydb: condition: service_healthy @@ -101,16 +137,16 @@ services: - RUST_LOG - ESPRESSO_CDN_SERVER_METRICS_PORT image: ghcr.io/espressosystems/espresso-sequencer/cdn-broker:main - command: - - cdn-broker - - -d - - redis://:changemeplease!!@keydb:6379 - - --public-advertise-endpoint - - broker-1:1738 - - --private-advertise-endpoint - - broker-1:1739 - - -m - - 0.0.0.0:$ESPRESSO_CDN_SERVER_METRICS_PORT + command: + - cdn-broker + - -d + - redis://:changemeplease!!@keydb:6379 + - --public-advertise-endpoint + - broker-1:1738 + - --private-advertise-endpoint + - broker-1:1739 + - -m + - 0.0.0.0:$ESPRESSO_CDN_SERVER_METRICS_PORT depends_on: keydb: condition: service_healthy @@ -122,10 +158,10 @@ services: - RUST_LOG - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL image: ghcr.io/espressosystems/espresso-sequencer/cdn-whitelist:main - command: - - cdn-whitelist - - -d - - redis://:changemeplease!!@keydb:6379 + command: + - cdn-whitelist + - -d + - redis://:changemeplease!!@keydb:6379 depends_on: keydb: condition: service_healthy @@ -138,7 +174,7 @@ services: - "$ESPRESSO_STATE_RELAY_SERVER_PORT:$ESPRESSO_STATE_RELAY_SERVER_PORT" environment: - ESPRESSO_STATE_RELAY_SERVER_PORT - - ESPRESSO_STATE_SIGNATURE_WEIGHT_THRESHOLD + - ESPRESSO_STATE_SIGNATURE_TOTAL_STAKE - RUST_LOG - RUST_LOG_FORMAT - ASYNC_STD_THREAD_COUNT @@ -150,11 +186,12 @@ services: environment: - ESPRESSO_PROVER_SERVICE_PORT - ESPRESSO_STATE_RELAY_SERVER_URL - - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL + - ESPRESSO_SEQUENCER_URL - ESPRESSO_STATE_PROVER_UPDATE_INTERVAL - ESPRESSO_SEQUENCER_L1_PROVIDER - ESPRESSO_SEQUENCER_ETH_MNEMONIC - ESPRESSO_SEQUENCER_LIGHTCLIENT_ADDRESS + - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY - MNEMONIC=$ESPRESSO_SEQUENCER_ETH_MNEMONIC - ESPRESSO_SEQUENCER_STATE_PROVER_ACCOUNT_INDEX - RAYON_NUM_THREADS=$PROVER_RAYON_NUM_THREADS @@ -163,13 +200,13 @@ services: - ASYNC_STD_THREAD_COUNT - RAYON_NUM_THREADS depends_on: - orchestrator: + sequencer0: condition: service_healthy state-relay-server: condition: service_healthy demo-l1-network: condition: service_healthy - deploy-contracts: + deploy-prover-contracts: condition: service_completed_successfully sequencer0: @@ -177,24 +214,27 @@ services: ports: - "$ESPRESSO_SEQUENCER_API_PORT:$ESPRESSO_SEQUENCER_API_PORT" - "$ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT:$ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT" - # Run the full API server with all modules, default storage - command: sequencer -- http -- query -- catchup -- status -- submit -- hotshot-events + # Run the full API server with all modules, Postgres storage + command: sequencer -- storage-sql -- http -- query -- catchup -- status -- submit -- hotshot-events -- config environment: + - ESPRESSO_SEQUENCER_GENESIS_FILE - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL - ESPRESSO_SEQUENCER_CDN_ENDPOINT - ESPRESSO_SEQUENCER_API_PORT + - ESPRESSO_SEQUENCER_MAX_CONNECTIONS - ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT - ESPRESSO_SEQUENCER_STATE_PEERS=http://sequencer1:$ESPRESSO_SEQUENCER_API_PORT - - ESPRESSO_SEQUENCER_STORAGE_PATH + - ESPRESSO_SEQUENCER_POSTGRES_HOST=sequencer-db-0 + - ESPRESSO_SEQUENCER_POSTGRES_USER=root + - ESPRESSO_SEQUENCER_POSTGRES_PASSWORD=password + - ESPRESSO_SEQUENCER_POSTGRES_DATABASE=sequencer - ESPRESSO_SEQUENCER_L1_PROVIDER + - ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE - ESPRESSO_STATE_RELAY_SERVER_URL - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_0 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_0 - ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS=0.0.0.0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_0 - ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS=sequencer0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_0 - - ESPRESSO_SEQUENCER_PREFUNDED_BUILDER_ACCOUNTS - - ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE - - ESPRESSO_SEQUENCER_BASE_FEE - ESPRESSO_SEQUENCER_IS_DA=true - RUST_LOG - RUST_LOG_FORMAT @@ -204,7 +244,7 @@ services: condition: service_healthy demo-l1-network: condition: service_healthy - sequencer-db: + sequencer-db-0: condition: service_healthy state-relay-server: condition: service_healthy @@ -214,30 +254,33 @@ services: condition: service_healthy marshal-0: condition: service_healthy + deploy-sequencer-contracts: + condition: service_completed_successfully sequencer1: image: ghcr.io/espressosystems/espresso-sequencer/sequencer:main ports: - "$ESPRESSO_SEQUENCER1_API_PORT:$ESPRESSO_SEQUENCER_API_PORT" - command: sequencer -- storage-sql -- http -- query -- catchup -- state + command: sequencer -- storage-sql -- http -- query -- catchup -- status -- state -- explorer environment: + - ESPRESSO_SEQUENCER_GENESIS_FILE - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL - ESPRESSO_SEQUENCER_CDN_ENDPOINT - ESPRESSO_SEQUENCER_API_PORT + - ESPRESSO_SEQUENCER_MAX_CONNECTIONS - ESPRESSO_SEQUENCER_API_PEERS=http://sequencer2:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_STATE_PEERS=http://sequencer2:$ESPRESSO_SEQUENCER_API_PORT - - ESPRESSO_SEQUENCER_POSTGRES_HOST=sequencer-db + - ESPRESSO_SEQUENCER_POSTGRES_HOST=sequencer-db-1 - ESPRESSO_SEQUENCER_POSTGRES_USER=root - ESPRESSO_SEQUENCER_POSTGRES_PASSWORD=password + - ESPRESSO_SEQUENCER_POSTGRES_DATABASE=sequencer - ESPRESSO_SEQUENCER_L1_PROVIDER + - ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE - ESPRESSO_STATE_RELAY_SERVER_URL - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_1 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_1 - ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS=0.0.0.0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_1 - ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS=sequencer1:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_1 - - ESPRESSO_SEQUENCER_PREFUNDED_BUILDER_ACCOUNTS - - ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE - - ESPRESSO_SEQUENCER_BASE_FEE - ESPRESSO_SEQUENCER_IS_DA=true - RUST_LOG - RUST_LOG_FORMAT @@ -247,6 +290,8 @@ services: condition: service_healthy demo-l1-network: condition: service_healthy + sequencer-db-1: + condition: service_healthy state-relay-server: condition: service_healthy broker-0: @@ -255,6 +300,8 @@ services: condition: service_healthy marshal-0: condition: service_healthy + deploy-sequencer-contracts: + condition: service_completed_successfully sequencer2: image: ghcr.io/espressosystems/espresso-sequencer/sequencer:main @@ -262,20 +309,20 @@ services: - "$ESPRESSO_SEQUENCER2_API_PORT:$ESPRESSO_SEQUENCER_API_PORT" command: sequencer -- http -- catchup -- status environment: + - ESPRESSO_SEQUENCER_GENESIS_FILE - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL - ESPRESSO_SEQUENCER_CDN_ENDPOINT - ESPRESSO_SEQUENCER_API_PORT + - ESPRESSO_SEQUENCER_MAX_CONNECTIONS - ESPRESSO_SEQUENCER_API_PEERS=http://sequencer1:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_STATE_PEERS=http://sequencer3:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_L1_PROVIDER + - ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE - ESPRESSO_STATE_RELAY_SERVER_URL - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_2 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_2 - ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS=0.0.0.0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_2 - ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS=sequencer2:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_2 - - ESPRESSO_SEQUENCER_PREFUNDED_BUILDER_ACCOUNTS - - ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE - - ESPRESSO_SEQUENCER_BASE_FEE - ESPRESSO_SEQUENCER_IS_DA=true - RUST_LOG - RUST_LOG_FORMAT @@ -293,6 +340,8 @@ services: condition: service_healthy marshal-0: condition: service_healthy + deploy-sequencer-contracts: + condition: service_completed_successfully sequencer3: image: ghcr.io/espressosystems/espresso-sequencer/sequencer:main @@ -300,19 +349,19 @@ services: - "$ESPRESSO_SEQUENCER3_API_PORT:$ESPRESSO_SEQUENCER_API_PORT" command: sequencer -- http -- catchup -- status environment: + - ESPRESSO_SEQUENCER_GENESIS_FILE - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL - ESPRESSO_SEQUENCER_CDN_ENDPOINT - ESPRESSO_SEQUENCER_API_PORT + - ESPRESSO_SEQUENCER_MAX_CONNECTIONS - ESPRESSO_SEQUENCER_STATE_PEERS=http://sequencer4:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_L1_PROVIDER + - ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE - ESPRESSO_STATE_RELAY_SERVER_URL - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_3 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_3 - ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS=0.0.0.0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_3 - ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS=sequencer3:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_3 - - ESPRESSO_SEQUENCER_PREFUNDED_BUILDER_ACCOUNTS - - ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE - - ESPRESSO_SEQUENCER_BASE_FEE - RUST_LOG - RUST_LOG_FORMAT - ASYNC_STD_THREAD_COUNT @@ -329,6 +378,8 @@ services: condition: service_healthy marshal-0: condition: service_healthy + deploy-sequencer-contracts: + condition: service_completed_successfully sequencer4: image: ghcr.io/espressosystems/espresso-sequencer/sequencer:main @@ -336,19 +387,19 @@ services: - "$ESPRESSO_SEQUENCER4_API_PORT:$ESPRESSO_SEQUENCER_API_PORT" command: sequencer -- http -- catchup -- status environment: + - ESPRESSO_SEQUENCER_GENESIS_FILE - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL - ESPRESSO_SEQUENCER_CDN_ENDPOINT - ESPRESSO_SEQUENCER_API_PORT + - ESPRESSO_SEQUENCER_MAX_CONNECTIONS - ESPRESSO_SEQUENCER_STATE_PEERS=http://sequencer0:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_L1_PROVIDER + - ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE - ESPRESSO_STATE_RELAY_SERVER_URL - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_4 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_4 - ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS=0.0.0.0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_4 - ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS=sequencer4:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_4 - - ESPRESSO_SEQUENCER_PREFUNDED_BUILDER_ACCOUNTS - - ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE - - ESPRESSO_SEQUENCER_BASE_FEE - RUST_LOG - RUST_LOG_FORMAT - ASYNC_STD_THREAD_COUNT @@ -365,6 +416,8 @@ services: condition: service_healthy marshal-0: condition: service_healthy + deploy-sequencer-contracts: + condition: service_completed_successfully commitment-task: image: ghcr.io/espressosystems/espresso-sequencer/commitment-task:main @@ -385,7 +438,7 @@ services: condition: service_healthy demo-l1-network: condition: service_healthy - deploy-contracts: + deploy-prover-contracts: condition: service_completed_successfully submit-transactions-public: @@ -402,6 +455,10 @@ services: depends_on: sequencer0: condition: service_healthy + # We don't directly depend on the builder, but transactions will not be finalized until it has + # started, so there is no point in starting before then. + permissionless-builder: + condition: service_healthy submit-transactions-private: image: ghcr.io/espressosystems/espresso-sequencer/submit-transactions:main @@ -428,27 +485,31 @@ services: environment: - ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_URL=http://sequencer0:$ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT - ESPRESSO_SEQUENCER_STATE_PEERS=http://sequencer0:$ESPRESSO_SEQUENCER_API_PORT + - ESPRESSO_BUILDER_GENESIS_FILE - ESPRESSO_BUILDER_ETH_MNEMONIC - ESPRESSO_BUILDER_ETH_ACCOUNT_INDEX - ESPRESSO_BUILDER_L1_PROVIDER - ESPRESSO_BUILDER_SERVER_PORT - - ESPRESSO_BUILDER_CHANNEL_CAPACITY + - ESPRESSO_BUILDER_TX_CHANNEL_CAPACITY + - ESPRESSO_BUILDER_EVENT_CHANNEL_CAPACITY - ESPRESSO_BUILDER_BOOTSTRAPPED_VIEW - ESPRESSO_BUILDER_WEBSERVER_RESPONSE_TIMEOUT_DURATION - ESPRESSO_BUILDER_BUFFER_VIEW_NUM_COUNT + - ESPRESSO_BUILDER_INIT_NODE_COUNT - RUST_LOG - RUST_LOG_FORMAT - ASYNC_STD_THREAD_COUNT depends_on: - sequencer0: - condition: service_healthy + fund-builder: + condition: service_completed_successfully nasty-client: image: ghcr.io/espressosystems/espresso-sequencer/nasty-client:main ports: - "$ESPRESSO_NASTY_CLIENT_PORT:$ESPRESSO_NASTY_CLIENT_PORT" environment: - - ESPRESSO_SEQUENCER_URL + # Point the nasty client at sequencer1, the only one running the state API. + - ESPRESSO_SEQUENCER_URL=http://sequencer1:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_NASTY_CLIENT_PORT - RUST_LOG - RUST_LOG_FORMAT @@ -457,14 +518,15 @@ services: sequencer0: condition: service_healthy - sequencer-db: + sequencer-db-0: image: postgres user: postgres ports: - - "$ESPRESSO_SEQUENCER_DB_PORT:5432" + - "$ESPRESSO_SEQUENCER0_DB_PORT:5432" environment: - POSTGRES_PASSWORD=password - POSTGRES_USER=root + - POSTGRES_DB=sequencer healthcheck: # Postgres can be falsely "ready" once before running init scripts. # See https://github.com/docker-library/postgres/issues/146 for discussion. @@ -472,3 +534,30 @@ services: interval: 5s timeout: 4s retries: 20 + + sequencer-db-1: + image: postgres + user: postgres + ports: + - "$ESPRESSO_SEQUENCER1_DB_PORT:5432" + environment: + - POSTGRES_PASSWORD=password + - POSTGRES_USER=root + - POSTGRES_DB=sequencer + healthcheck: + # Postgres can be falsely "ready" once before running init scripts. + # See https://github.com/docker-library/postgres/issues/146 for discussion. + test: "pg_isready -U root && sleep 1 && pg_isready -U root" + interval: 5s + timeout: 4s + retries: 20 + + block-explorer: + image: ghcr.io/espressosystems/espresso-block-explorer:main + ports: + - "$ESPRESSO_BLOCK_EXPLORER_PORT:3000" + environment: + - QUERY_SERVICE_URI:http://localhost:$ESPRESSO_SEQUENCER1_API_PORT/v0/ + depends_on: + sequencer1: + condition: service_healthy diff --git a/docker/espresso-bridge.Dockerfile b/docker/espresso-bridge.Dockerfile new file mode 100644 index 000000000..ad7db1839 --- /dev/null +++ b/docker/espresso-bridge.Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:jammy + +ARG TARGETARCH + +RUN apt-get update \ + && apt-get install -y curl libcurl4 wait-for-it tini \ + && rm -rf /var/lib/apt/lists/* +ENTRYPOINT ["tini", "--"] + +COPY target/$TARGETARCH/release/espresso-bridge /bin/espresso-bridge +RUN chmod +x /bin/espresso-bridge + +RUN ln -s /bin/espresso-bridge /bin/bridge + +CMD [ "/bin/espresso-bridge"] diff --git a/docker/espresso-dev-node.Dockerfile b/docker/espresso-dev-node.Dockerfile new file mode 100644 index 000000000..7b5982857 --- /dev/null +++ b/docker/espresso-dev-node.Dockerfile @@ -0,0 +1,26 @@ +FROM postgres + +ARG TARGETARCH + +RUN apt-get update \ + && apt-get install -y curl libcurl4 wait-for-it tini \ + && rm -rf /var/lib/apt/lists/* +ENTRYPOINT ["tini", "--"] + +# Download an SRS file to avoid download at runtime +ENV AZTEC_SRS_PATH=/kzg10-aztec20-srs-1048584.bin +RUN curl -LO https://github.com/EspressoSystems/ark-srs/releases/download/v0.2.0/$AZTEC_SRS_PATH + +COPY target/$TARGETARCH/release/espresso-dev-node /bin/espresso-dev-node +RUN chmod +x /bin/espresso-dev-node + +# When running as a Docker service, we always want a healthcheck endpoint, so set a default for the +# port that the HTTP server will run on. This can be overridden in any given deployment environment. +ENV ESPRESSO_SEQUENCER_API_PORT=8770 +HEALTHCHECK --interval=1s --timeout=1s --retries=100 CMD curl --fail http://localhost:${ESPRESSO_SEQUENCER_API_PORT}/status/block-height || exit 1 + +EXPOSE 8770 +EXPOSE 8771 +EXPOSE 8772 + +CMD [ "/bin/espresso-dev-node"] diff --git a/docker/permissionless-builder.Dockerfile b/docker/permissionless-builder.Dockerfile index 4140ae5f7..82424dd6e 100644 --- a/docker/permissionless-builder.Dockerfile +++ b/docker/permissionless-builder.Dockerfile @@ -7,6 +7,10 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT ["tini", "--"] +# Install genesis files for all supported configurations. The desired configuration can be chosen by +# setting `ESPRESSO_BUILDER_GENESIS_FILE`. +COPY data/genesis /genesis + # Download an SRS file to avoid download at runtime ENV AZTEC_SRS_PATH=/kzg10-aztec20-srs-1048584.bin RUN curl -LO https://github.com/EspressoSystems/ark-srs/releases/download/v0.2.0/$AZTEC_SRS_PATH diff --git a/docker/scripts/sequencer-awssecretsmanager.sh b/docker/scripts/sequencer-awssecretsmanager.sh new file mode 100644 index 000000000..582cd36f2 --- /dev/null +++ b/docker/scripts/sequencer-awssecretsmanager.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -eEu -o pipefail + +if [[ -v ESPRESSO_SEQUENCER_GENESIS_SECRET ]]; then + echo "Loading genesis file from AWS secrets manager" + aws secretsmanager get-secret-value --secret-id ${ESPRESSO_SEQUENCER_GENESIS_SECRET} --query SecretString --output text | tee /genesis/injected.toml >/dev/null +fi + +/bin/sequencer "$@" diff --git a/docker/sequencer.Dockerfile b/docker/sequencer.Dockerfile index e84367152..eac4ec5f5 100644 --- a/docker/sequencer.Dockerfile +++ b/docker/sequencer.Dockerfile @@ -23,6 +23,14 @@ RUN chmod +x /bin/keygen COPY target/$TARGETARCH/release/pub-key /bin/pub-key RUN chmod +x /bin/pub-key +# Install genesis files for all supported configurations. The desired configuration can be chosen by +# setting `ESPRESSO_SEQUENCER_GENESIS_FILE`. +COPY data/genesis /genesis + +# Allow injecting a genesis file with aws secretsmanager +# Set `ESPRESSO_SEQUENCER_GENESIS_SECRET` +COPY docker/scripts/sequencer-awssecretsmanager.sh /bin/sequencer-awssecretsmanager.sh + # Set a path to save the consensus config on startup. # # Upon restart, the config will be loaded from this file and the node will be able to resume diff --git a/flake.lock b/flake.lock index 832aab8c3..cef230226 100644 --- a/flake.lock +++ b/flake.lock @@ -196,11 +196,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1712135466, - "narHash": "sha256-+xFfYk17EI0zZTGmhh3MyeSpl7RVohoVp/4HaSvGj4I=", + "lastModified": 1714727549, + "narHash": "sha256-CWXRTxxcgMfQubJugpeg3yVWIfm70MYTtgaKWKgD60U=", "owner": "shazow", "repo": "foundry.nix", - "rev": "ece7c960a440c6725a7a5576d1f49a5fabde3747", + "rev": "47cf189ec395eda4b3e0623179d1075c8027ca97", "type": "github" }, "original": { @@ -476,11 +476,11 @@ "nixpkgs": "nixpkgs_6" }, "locked": { - "lastModified": 1713838472, - "narHash": "sha256-lCdDz6/YgyXdFRHall3P+dCETRpfz3Pi9eREnA9RX6k=", + "lastModified": 1714616033, + "narHash": "sha256-JcWAjIDl3h0bE/pII0emeHwokTeBl+SWrzwrjoRu7a0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "28a9436d356181603fb0d333565431c3d952f299", + "rev": "3e416d5067ba31ff8ac31eeb763e4388bdf45089", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 59c458a30..777dba371 100644 --- a/flake.nix +++ b/flake.nix @@ -49,10 +49,12 @@ # node=error: disable noisy anvil output RUST_LOG = "info,libp2p=off,isahc=error,surf=error,node=error"; RUST_BACKTRACE = 1; - RUSTFLAGS = - " --cfg async_executor_impl=\"async-std\" --cfg async_channel_impl=\"async-std\" --cfg hotshot_example"; + ASYNC_FLAGS = " --cfg async_executor_impl=\"async-std\" --cfg async_channel_impl=\"async-std\" "; + RUSTFLAGS = "${ASYNC_FLAGS} --cfg hotshot_example"; + RUSTDOCFLAGS = ASYNC_FLAGS; # Use a distinct target dir for builds from within nix shells. CARGO_TARGET_DIR = "target/nix"; + rustEnvVars = { inherit RUST_LOG RUST_BACKTRACE RUSTFLAGS RUSTDOCFLAGS CARGO_TARGET_DIR; }; solhintPkg = { buildNpmPackage, fetchFromGitHub }: buildNpmPackage rec { @@ -90,10 +92,11 @@ inherit overlays localSystem crossSystem; }; in - import ./cross-shell.nix { - inherit pkgs; - inherit RUST_LOG RUST_BACKTRACE RUSTFLAGS CARGO_TARGET_DIR; - }; + import ./cross-shell.nix + { + inherit pkgs; + envVars = rustEnvVars; + }; in with pkgs; { checks = { @@ -124,8 +127,7 @@ cargo-clippy = { enable = true; description = "Run clippy"; - entry = - "cargo clippy --workspace --all-features --all-targets -- -D warnings"; + entry = "just clippy"; types_or = [ "rust" "toml" ]; pass_filenames = false; }; @@ -154,15 +156,16 @@ enable = true; description = "Enforce markdown formatting"; entry = "prettier -w"; - types_or = [ "markdown" ]; + types_or = [ "markdown" "ts" ]; pass_filenames = true; }; spell-checking = { enable = true; description = "Spell checking"; - entry = "typos"; + # --force-exclude to exclude excluded files if they are passed as arguments + entry = "typos --force-exclude"; pass_filenames = true; - excludes = [ "contract-bindings/" ]; + # Add excludes to the .typos.toml file instead }; nixpkgs-fmt.enable = true; }; @@ -180,7 +183,7 @@ ''; solc = pkgs.solc-bin.latest; in - mkShell { + mkShell (rustEnvVars // { buildInputs = [ # Rust dependencies pkg-config @@ -218,12 +221,14 @@ nodePackages.prettier solhint (python3.withPackages (ps: with ps; [ black ])) - + yarn ] ++ lib.optionals stdenv.isDarwin [ darwin.apple_sdk.frameworks.SystemConfiguration ] ++ lib.optionals (!stdenv.isDarwin) [ cargo-watch ] # broken on OSX ; shellHook = '' + # Add node binaries to PATH + export PATH="$PWD/node_modules/.bin:$PATH" # Prevent cargo aliases from using programs in `~/.cargo` to avoid conflicts # with rustup installations. export CARGO_HOME=$HOME/.cargo-nix @@ -231,8 +236,7 @@ '' + self.checks.${system}.pre-commit-check.shellHook; RUST_SRC_PATH = "${stableToolchain}/lib/rustlib/src/rust/library"; FOUNDRY_SOLC = "${solc}/bin/solc"; - inherit RUST_LOG RUST_BACKTRACE RUSTFLAGS CARGO_TARGET_DIR; - }; + }); devShells.crossShell = crossShell { config = "x86_64-unknown-linux-musl"; }; devShells.armCrossShell = @@ -243,7 +247,7 @@ extensions = [ "rustfmt" "clippy" "llvm-tools-preview" "rust-src" ]; }; in - mkShell { + mkShell (rustEnvVars // { buildInputs = [ # Rust dependencies pkg-config @@ -252,13 +256,12 @@ protobuf # to compile libp2p-autonat toolchain ]; - inherit RUST_LOG RUST_BACKTRACE RUSTFLAGS CARGO_TARGET_DIR; - }; + }); devShells.coverage = let toolchain = pkgs.rust-bin.nightly.latest.minimal; in - mkShell { + mkShell (rustEnvVars // { buildInputs = [ # Rust dependencies pkg-config @@ -268,13 +271,12 @@ toolchain grcov ]; - inherit RUST_LOG RUST_BACKTRACE RUSTFLAGS CARGO_TARGET_DIR; CARGO_INCREMENTAL = "0"; shellHook = '' RUSTFLAGS="$RUSTFLAGS -Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests -Cdebuginfo=2" ''; RUSTDOCFLAGS = "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests"; - }; + }); devShells.rustShell = let @@ -282,7 +284,7 @@ extensions = [ "rustfmt" "clippy" "llvm-tools-preview" "rust-src" ]; }; in - mkShell { + mkShell (rustEnvVars // { buildInputs = [ # Rust dependencies pkg-config @@ -291,7 +293,6 @@ protobuf # to compile libp2p-autonat stableToolchain ]; - inherit RUST_LOG RUST_BACKTRACE RUSTFLAGS CARGO_TARGET_DIR; - }; + }); }); } diff --git a/foundry.toml b/foundry.toml index e2b19ad85..061cd3fd9 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,7 +9,7 @@ libs = ['contracts/lib'] script = "contracts/script" broadcast = "contracts/broadcast" cache_path = "contracts/cache" -optimizer_runs = 2300 # Increasing the number of runs saves gas but increases the size of the contract +optimizer_runs = 0 # Increasing the number of runs saves gas but increases the size of the contract # Version should match the solc installed via flake, otherwise the contract # artifacts may differ slightly. solc = "0.8.23" diff --git a/hotshot-state-prover/Cargo.toml b/hotshot-state-prover/Cargo.toml index d832988b4..4947d7ead 100644 --- a/hotshot-state-prover/Cargo.toml +++ b/hotshot-state-prover/Cargo.toml @@ -6,7 +6,7 @@ authors = { workspace = true } edition = { workspace = true } [dependencies] -anyhow = "1.0" +anyhow = { workspace = true } ark-bn254 = { workspace = true } ark-ec = { workspace = true } ark-ed-on-bn254 = { workspace = true } @@ -26,21 +26,25 @@ displaydoc = { version = "0.2.3", default-features = false } es-version = { workspace = true } ethers = { workspace = true } futures = { workspace = true } -hotshot-contract-adapter = { path = "../contracts/rust/adapter" } +hotshot-contract-adapter = { workspace = true } hotshot-orchestrator = { workspace = true } hotshot-stake-table = { workspace = true } hotshot-types = { workspace = true } itertools = { workspace = true } +jf-crhf = { workspace = true } +jf-pcs = { workspace = true } jf-plonk = { workspace = true } -jf-primitives = { workspace = true } jf-relation = { workspace = true } +jf-rescue = { workspace = true, features = ["gadgets"] } +jf-signature = { workspace = true, features = ["schnorr", "bls", "gadgets"] } jf-utils = { workspace = true } rand_chacha = { workspace = true } +reqwest = { workspace = true } sequencer-utils = { path = "../utils" } serde = { workspace = true } snafu = { workspace = true } surf-disco = { workspace = true } -tagged-base64 = { git = "https://github.com/EspressoSystems/tagged-base64", tag = "0.3.4" } +tagged-base64 = { workspace = true } tide-disco = { workspace = true } time = { workspace = true } toml = "0.8" @@ -51,4 +55,4 @@ vbs = { workspace = true } [features] default = ["parallel"] std = ["ark-std/std", "ark-ff/std"] -parallel = ["jf-primitives/parallel", "jf-utils/parallel", "ark-ff/parallel"] +parallel = ["jf-signature/parallel", "jf-utils/parallel", "ark-ff/parallel"] diff --git a/hotshot-state-prover/src/bin/state-prover.rs b/hotshot-state-prover/src/bin/state-prover.rs index 1d9f94fb2..f1f25a027 100644 --- a/hotshot-state-prover/src/bin/state-prover.rs +++ b/hotshot-state-prover/src/bin/state-prover.rs @@ -29,6 +29,10 @@ struct Args { #[clap(short, long = "freq", value_parser = parse_duration, default_value = "10m", env = "ESPRESSO_STATE_PROVER_UPDATE_INTERVAL")] update_interval: Duration, + /// Interval between retries if a state update fails + #[clap(long = "retry-freq", value_parser = parse_duration, default_value = "2s", env = "ESPRESSO_STATE_PROVER_RETRY_INTERVAL")] + retry_interval: Duration, + /// URL of layer 1 Ethereum JSON-RPC provider. #[clap( long, @@ -53,14 +57,14 @@ struct Args { )] eth_account_index: u32, - /// URL of the HotShot orchestrator. + /// URL of a sequencer node that is currently providing the HotShot config. + /// This is used to initialize the stake table. #[clap( - short, long, - env = "ESPRESSO_SEQUENCER_ORCHESTRATOR_URL", - default_value = "http://localhost:8080" + env = "ESPRESSO_SEQUENCER_URL", + default_value = "http://localhost:24000" )] - pub orchestrator_url: Url, + pub sequencer_url: Url, /// If daemon and provided, the service will run a basic HTTP server on the given port. /// @@ -97,9 +101,10 @@ async fn main() { let provider = Provider::::try_from(args.l1_provider.to_string()).unwrap(); let chain_id = provider.get_chainid().await.unwrap().as_u64(); let config = StateProverConfig { - relay_server: args.relay_server.clone(), + relay_server: args.relay_server, update_interval: args.update_interval, - l1_provider: args.l1_provider.clone(), + retry_interval: args.retry_interval, + l1_provider: args.l1_provider, light_client_address: args.light_client_address, eth_signing_key: MnemonicBuilder::::default() .phrase(args.eth_mnemonic.as_str()) @@ -110,16 +115,20 @@ async fn main() { .with_chain_id(chain_id) .signer() .clone(), - orchestrator_url: args.orchestrator_url, + sequencer_url: args.sequencer_url, port: args.port, stake_table_capacity: args.stake_table_capacity, }; if args.daemon { // Launching the prover service daemon - run_prover_service(config, SEQUENCER_VERSION).await; + if let Err(err) = run_prover_service(config, SEQUENCER_VERSION).await { + tracing::error!("Error running prover service: {:?}", err); + }; } else { // Run light client state update once - run_prover_once(config, SEQUENCER_VERSION).await; + if let Err(err) = run_prover_once(config, SEQUENCER_VERSION).await { + tracing::error!("Error running prover once: {:?}", err); + }; } } diff --git a/hotshot-state-prover/src/circuit.rs b/hotshot-state-prover/src/circuit.rs index cdd262d8b..e1e2603b9 100644 --- a/hotshot-state-prover/src/circuit.rs +++ b/hotshot-state-prover/src/circuit.rs @@ -5,16 +5,13 @@ use ark_ff::PrimeField; use ark_std::borrow::Borrow; use ethers::types::U256; use hotshot_types::light_client::{GenericLightClientState, GenericPublicInput}; -use jf_plonk::errors::PlonkError; -use jf_primitives::{ - circuit::{ - rescue::RescueNativeGadget, - signature::schnorr::{SignatureGadget, VerKeyVar}, - }, - rescue::RescueParameter, - signatures::schnorr::{Signature, VerKey as SchnorrVerKey}, +use jf_plonk::PlonkError; +use jf_relation::{BoolVar, Circuit, CircuitError, PlonkCircuit, Variable}; +use jf_rescue::{gadgets::RescueNativeGadget, RescueParameter}; +use jf_signature::{ + gadgets::schnorr::{SignatureGadget, VerKeyVar}, + schnorr::{Signature, VerKey as SchnorrVerKey}, }; -use jf_relation::{errors::CircuitError, BoolVar, Circuit, PlonkCircuit, Variable}; /// Lossy conversion of a U256 into a field element. pub(crate) fn u256_to_field(v: &U256) -> F { @@ -380,11 +377,13 @@ mod tests { use ark_ed_on_bn254::EdwardsConfig as Config; use ethers::types::U256; use hotshot_types::traits::stake_table::{SnapshotVersion, StakeTableScheme}; - use jf_primitives::{ - crhf::{VariableLengthRescueCRHF, CRHF}, - errors::PrimitivesError, - signatures::{schnorr::Signature, SchnorrSignatureScheme, SignatureScheme}, + use jf_crhf::CRHF; + use jf_rescue::crhf::VariableLengthRescueCRHF; + use jf_signature::{ + schnorr::{SchnorrSignatureScheme, Signature}, + SignatureScheme, }; + use jf_relation::Circuit; use jf_utils::test_rng; @@ -425,7 +424,7 @@ mod tests { let sigs = state_keys .iter() .map(|(key, _)| SchnorrSignatureScheme::::sign(&(), key, state_msg, &mut prng)) - .collect::, PrimitivesError>>() + .collect::, _>>() .unwrap(); // bit vector with total weight 26 @@ -532,7 +531,7 @@ mod tests { .map(|(key, _)| { SchnorrSignatureScheme::::sign(&(), key, bad_state_msg, &mut prng) }) - .collect::, PrimitivesError>>() + .collect::, _>>() .unwrap(); let (bad_circuit, public_inputs) = build( &entries, @@ -557,7 +556,7 @@ mod tests { .map(|(key, _)| { SchnorrSignatureScheme::::sign(&(), key, wrong_state_msg, &mut prng) }) - .collect::, PrimitivesError>>() + .collect::, _>>() .unwrap(); let (bad_circuit, public_inputs) = build( &entries, diff --git a/hotshot-state-prover/src/mock_ledger.rs b/hotshot-state-prover/src/mock_ledger.rs index 82e2b7210..0b7b061f7 100644 --- a/hotshot-state-prover/src/mock_ledger.rs +++ b/hotshot-state-prover/src/mock_ledger.rs @@ -16,28 +16,31 @@ use hotshot_contract_adapter::jellyfish::{field_to_u256, open_key, u256_to_field use hotshot_contract_adapter::light_client::ParsedLightClientState; use hotshot_stake_table::vec_based::StakeTable; -use crate::{generate_state_update_proof, preprocess, Proof, VerifyingKey}; +use crate::{ + generate_state_update_proof, preprocess, service::one_honest_threshold, Proof, VerifyingKey, +}; use hotshot_types::traits::stake_table::StakeTableScheme; use hotshot_types::{ light_client::{GenericLightClientState, GenericPublicInput, LightClientState}, traits::stake_table::SnapshotVersion, }; use itertools::izip; +use jf_pcs::prelude::UnivariateUniversalParams; use jf_plonk::proof_system::{PlonkKzgSnark, UniversalSNARK}; use jf_plonk::transcript::SolidityTranscript; -use jf_primitives::pcs::prelude::UnivariateUniversalParams; -use jf_primitives::signatures::schnorr::Signature; -use jf_primitives::signatures::{ +use jf_relation::{Arithmetization, Circuit, PlonkCircuit}; +use jf_signature::schnorr::Signature; +use jf_signature::{ bls_over_bn254::{BLSOverBN254CurveSignatureScheme, VerKey as BLSVerKey}, - SchnorrSignatureScheme, SignatureScheme, + schnorr::SchnorrSignatureScheme, + SignatureScheme, }; -use jf_relation::{Arithmetization, Circuit, PlonkCircuit}; use jf_utils::test_rng; use std::collections::HashMap; type F = ark_ed_on_bn254::Fq; -type SchnorrVerKey = jf_primitives::signatures::schnorr::VerKey; -type SchnorrSignKey = jf_primitives::signatures::schnorr::SignKey; +type SchnorrVerKey = jf_signature::schnorr::VerKey; +type SchnorrSignKey = jf_signature::schnorr::SignKey; /// Stake table capacity used for testing pub const STAKE_TABLE_CAPACITY: usize = 10; @@ -85,7 +88,8 @@ impl MockLedger { key_archive.insert(qc_keys[i], state_keys[i].0.clone()); } let st = stake_table_for_testing(&qc_keys, &state_keys); - let threshold = st.total_stake(SnapshotVersion::LastEpochStart).unwrap() * 2 / 3; + let threshold = + one_honest_threshold(st.total_stake(SnapshotVersion::LastEpochStart).unwrap()); // arbitrary commitment values as they don't affect logic being tested let block_comm_root = F::from(1234); @@ -119,12 +123,11 @@ impl MockLedger { { self.epoch += 1; self.st.advance(); - self.threshold = self - .st - .total_stake(SnapshotVersion::LastEpochStart) - .unwrap() - * 2 - / 3; + self.threshold = one_honest_threshold( + self.st + .total_stake(SnapshotVersion::LastEpochStart) + .unwrap(), + ); } let new_root = self.new_dummy_comm(); diff --git a/hotshot-state-prover/src/service.rs b/hotshot-state-prover/src/service.rs index b877edcf0..df5263767 100644 --- a/hotshot-state-prover/src/service.rs +++ b/hotshot-state-prover/src/service.rs @@ -1,11 +1,11 @@ //! A light client prover service use crate::snark::{generate_state_update_proof, Proof, ProvingKey}; -use anyhow::anyhow; +use anyhow::{anyhow, Context, Result}; use async_std::{ io, sync::Arc, - task::{sleep, spawn}, + task::{sleep, spawn, spawn_blocking}, }; use contract_bindings::light_client::{LightClient, LightClientErrors}; use displaydoc::Display; @@ -20,10 +20,8 @@ use ethers::{ use futures::FutureExt; use hotshot_contract_adapter::jellyfish::{u256_to_field, ParsedPlonkProof}; use hotshot_contract_adapter::light_client::ParsedLightClientState; -use hotshot_orchestrator::OrchestratorVersion; use hotshot_stake_table::vec_based::config::FieldType; use hotshot_stake_table::vec_based::StakeTable; -use hotshot_types::signature_key::BLSPubKey; use hotshot_types::traits::stake_table::{SnapshotVersion, StakeTableError, StakeTableScheme as _}; use hotshot_types::{ light_client::{ @@ -32,11 +30,13 @@ use hotshot_types::{ }, traits::signature_key::StakeTableEntryType, }; +use hotshot_types::{signature_key::BLSPubKey, PeerConfig}; +use jf_pcs::prelude::UnivariateUniversalParams; use jf_plonk::errors::PlonkError; -use jf_primitives::constants::CS_ID_SCHNORR; -use jf_primitives::pcs::prelude::UnivariateUniversalParams; use jf_relation::Circuit as _; +use jf_signature::constants::CS_ID_SCHNORR; +use serde::Deserialize; use std::{ iter, time::{Duration, Instant}, @@ -52,8 +52,6 @@ type F = ark_ed_on_bn254::Fq; /// A wallet with local signer and connected to network via http pub type L1Wallet = SignerMiddleware, LocalWallet>; -type NetworkConfig = hotshot_orchestrator::config::NetworkConfig; - /// Configuration/Parameters used for hotshot state prover #[derive(Debug, Clone)] pub struct StateProverConfig { @@ -61,14 +59,17 @@ pub struct StateProverConfig { pub relay_server: Url, /// Interval between light client state update pub update_interval: Duration, + /// Interval between retries if a state update fails + pub retry_interval: Duration, /// URL of layer 1 Ethereum JSON-RPC provider. pub l1_provider: Url, /// Address of LightClient contract on layer 1. pub light_client_address: Address, /// Transaction signing key for Ethereum pub eth_signing_key: SigningKey, - /// Address off the hotshot orchestrator, used for stake table initialization. - pub orchestrator_url: Url, + /// URL of a node that is currently providing the HotShot config. + /// This is used to initialize the stake table. + pub sequencer_url: Url, /// If daemon and provided, the service will run a basic HTTP server on the given port. /// /// The server provides healthcheck and version endpoints. @@ -77,6 +78,12 @@ pub struct StateProverConfig { pub stake_table_capacity: usize, } +#[inline] +/// A helper function to compute the quorum threshold given a total amount of stake. +pub fn one_honest_threshold(total_stake: U256) -> U256 { + total_stake / 3 + 1 +} + pub fn init_stake_table( bls_keys: &[BLSPubKey], state_keys: &[StateVerKey], @@ -95,67 +102,81 @@ pub fn init_stake_table( Ok(st) } -async fn init_stake_table_from_orchestrator( - orchestrator_url: &Url, +#[derive(Debug, Deserialize)] +pub struct PublicHotShotConfig { + pub known_nodes_with_stake: Vec>, +} + +/// Initialize the stake table from a sequencer node that +/// is currently providing the HotShot config. +/// +/// Does not error, runs until the stake table is provided. +async fn init_stake_table_from_sequencer( + sequencer_url: &Url, stake_table_capacity: usize, -) -> StakeTable { - tracing::info!("Initializing stake table from HotShot orchestrator."); - let client = Client::::new(orchestrator_url.clone()); - loop { - match client.get::("api/peer_pub_ready").send().await { - Ok(true) => { - match client - .get::("api/get_config_after_peer_collected") - .send() - .await - { - Ok(config) => { - let mut st = StakeTable::::new( - stake_table_capacity, - ); - tracing::debug!("{}", config.config.known_nodes_with_stake.len()); - config - .config - .known_nodes_with_stake - .into_iter() - .for_each(|config| { - st.register( - *config.stake_table_entry.get_key(), - config.stake_table_entry.get_stake(), - config.state_ver_key, - ) - .expect("Key registration shouldn't fail."); - }); - st.advance(); - st.advance(); - return st; - } - Err(e) => { - tracing::warn!("Orchestrator error: {e}, retrying."); - } +) -> Result> { + tracing::info!("Initializing stake table from node at {sequencer_url}"); + + // Construct the URL to fetch the network config + let config_url = sequencer_url + .join("/v0/config/hotshot") + .with_context(|| "Invalid URL")?; + + // Request the configuration until it is successful + let network_config: PublicHotShotConfig = loop { + match reqwest::get(config_url.clone()).await { + Ok(resp) => match resp.json::().await { + Ok(config) => break config, + Err(e) => { + tracing::error!("Failed to parse the network config: {e}"); + sleep(Duration::from_secs(5)).await; } - } - Ok(false) => { - tracing::info!("Peers' keys are not ready, retrying."); - } + }, Err(e) => { - tracing::warn!("Orchestrator error {e}, retrying."); + tracing::error!("Failed to fetch the network config: {e}"); + sleep(Duration::from_secs(5)).await; } } - sleep(Duration::from_secs(2)).await; + }; + + // Create empty stake table + let mut st = StakeTable::::new(stake_table_capacity); + + // Populate the stake table + for node in network_config.known_nodes_with_stake.into_iter() { + st.register( + *node.stake_table_entry.key(), + node.stake_table_entry.stake(), + node.state_ver_key, + ) + .expect("Key registration shouldn't fail."); } + + // Advance the stake table + st.advance(); + st.advance(); + + Ok(st) } pub async fn light_client_genesis( - orchestrator_url: &Url, + sequencer_url: &Url, stake_table_capacity: usize, ) -> anyhow::Result { - let st = init_stake_table_from_orchestrator(orchestrator_url, stake_table_capacity).await; + let st = init_stake_table_from_sequencer(sequencer_url, stake_table_capacity) + .await + .with_context(|| "Failed to initialize stake table")?; + light_client_genesis_from_stake_table(st) +} + +#[inline] +pub fn light_client_genesis_from_stake_table( + st: StakeTable, +) -> anyhow::Result { let (bls_comm, schnorr_comm, stake_comm) = st .commitment(SnapshotVersion::LastEpochStart) .expect("Commitment computation shouldn't fail."); - let threshold = st.total_stake(SnapshotVersion::LastEpochStart)? * 2 / 3; - + let threshold = one_honest_threshold(st.total_stake(SnapshotVersion::LastEpochStart)?); let pi = vec![ u256_to_field(threshold), F::from(0_u64), // Arbitrary value for view number @@ -274,13 +295,14 @@ pub async fn submit_state_and_proof( pub async fn sync_state( st: &StakeTable, - proving_key: &ProvingKey, + proving_key: Arc, relay_server_client: &Client, config: &StateProverConfig, ) -> Result<(), ProverError> { tracing::info!("Start syncing light client state."); let bundle = fetch_latest_state(relay_server_client).await?; + tracing::info!("Bundle accumulated weight: {}", bundle.accumulated_weight); tracing::info!("Latest HotShot block height: {}", bundle.state.block_height); let old_state = read_contract_state(config).await?; tracing::info!( @@ -294,7 +316,7 @@ pub async fn sync_state( tracing::debug!("Old state: {old_state:?}"); tracing::debug!("New state: {:?}", bundle.state); - let threshold = st.total_stake(SnapshotVersion::LastEpochStart)? * 2 / 3; + let threshold = one_honest_threshold(st.total_stake(SnapshotVersion::LastEpochStart)?); tracing::info!("Threshold before syncing state: {}", threshold); let entries = st .try_iter(SnapshotVersion::LastEpochStart) @@ -322,24 +344,22 @@ pub async fn sync_state( )); } - // TODO this assert fails. See https://github.com/EspressoSystems/espresso-sequencer/issues/1161 - // assert_eq!( - // bundle.state.stake_table_comm, - // st.commitment(SnapshotVersion::LastEpochStart).unwrap() - // ); - tracing::info!("Collected latest state and signatures. Start generating SNARK proof."); let proof_gen_start = Instant::now(); - let (proof, public_input) = generate_state_update_proof::<_, _, _, _>( - &mut ark_std::rand::thread_rng(), - proving_key, - &entries, - signer_bit_vec, - signatures, - &bundle.state, - &threshold, - config.stake_table_capacity, - )?; + let stake_table_capacity = config.stake_table_capacity; + let (proof, public_input) = spawn_blocking(move || { + generate_state_update_proof::<_, _, _, _>( + &mut ark_std::rand::thread_rng(), + &proving_key, + &entries, + signer_bit_vec, + signatures, + &bundle.state, + &threshold, + stake_table_capacity, + ) + }) + .await?; let proof_gen_elapsed = Instant::now().signed_duration_since(proof_gen_start); tracing::info!("Proof generation completed. Elapsed: {proof_gen_elapsed:.3}"); @@ -376,11 +396,14 @@ fn start_http_server( pub async fn run_prover_service( config: StateProverConfig, bind_version: Ver, -) { +) -> Result<()> { + let stake_table_capacity = config.stake_table_capacity; + tracing::info!("Stake table capacity: {}", stake_table_capacity); // TODO(#1022): maintain the following stake table let st = Arc::new( - init_stake_table_from_orchestrator(&config.orchestrator_url, config.stake_table_capacity) - .await, + init_stake_table_from_sequencer(&config.sequencer_url, stake_table_capacity) + .await + .with_context(|| "Failed to initialize stake table")?, ); tracing::info!("Light client address: {:?}", config.light_client_address); @@ -394,38 +417,41 @@ pub async fn run_prover_service( } } - let proving_key = async_std::task::block_on(async move { - Arc::new(load_proving_key(config.stake_table_capacity)) - }); + let proving_key = + spawn_blocking(move || Arc::new(load_proving_key(stake_table_capacity))).await; let update_interval = config.update_interval; + let retry_interval = config.retry_interval; loop { - let st = st.clone(); - let proving_key = proving_key.clone(); - let relay_server_client = relay_server_client.clone(); - let config = config.clone(); - // Use block_on to avoid blocking the async runtime with this computationally heavy task - async_std::task::block_on(async move { - if let Err(err) = sync_state(&st, &proving_key, &relay_server_client, &config).await { - tracing::error!("Cannot sync the light client state: {}", err); - } - }); - tracing::info!("Sleeping for {:?}", update_interval); - sleep(update_interval).await; + if let Err(err) = sync_state(&st, proving_key.clone(), &relay_server_client, &config).await + { + tracing::error!("Cannot sync the light client state, will retry: {}", err); + sleep(retry_interval).await; + } else { + tracing::info!("Sleeping for {:?}", update_interval); + sleep(update_interval).await; + } } } /// Run light client state prover once -pub async fn run_prover_once(config: StateProverConfig, _: Ver) { - let st = - init_stake_table_from_orchestrator(&config.orchestrator_url, config.stake_table_capacity) - .await; - let proving_key = load_proving_key(config.stake_table_capacity); +pub async fn run_prover_once( + config: StateProverConfig, + _: Ver, +) -> Result<()> { + let st = init_stake_table_from_sequencer(&config.sequencer_url, config.stake_table_capacity) + .await + .with_context(|| "Failed to initialize stake table")?; + let stake_table_capacity = config.stake_table_capacity; + let proving_key = + spawn_blocking(move || Arc::new(load_proving_key(stake_table_capacity))).await; let relay_server_client = Client::::new(config.relay_server.clone()); - sync_state(&st, &proving_key, &relay_server_client, &config) + sync_state(&st, proving_key, &relay_server_client, &config) .await .expect("Error syncing the light client state."); + + Ok(()) } #[derive(Debug, Display)] @@ -484,12 +510,13 @@ mod test { }; use hotshot_stake_table::vec_based::StakeTable; use hotshot_types::light_client::StateSignKey; - use jf_primitives::signatures::{SchnorrSignatureScheme, SignatureScheme}; + use jf_signature::{schnorr::SchnorrSignatureScheme, SignatureScheme}; use jf_utils::test_rng; use sequencer_utils::deployer; const STAKE_TABLE_CAPACITY_FOR_TEST: usize = 10; const BLOCKS_PER_EPOCH: u32 = 10; + const NUM_INIT_VALIDATORS: u32 = (STAKE_TABLE_CAPACITY_FOR_TEST / 2) as u32; /// Init a meaningful ledger state that prover can generate future valid proof. @@ -624,10 +651,11 @@ mod test { Self { relay_server: Url::parse("http://localhost").unwrap(), update_interval: Duration::default(), + retry_interval: Duration::default(), l1_provider: Url::parse("http://localhost").unwrap(), light_client_address: Address::default(), eth_signing_key: SigningKey::random(&mut test_rng()), - orchestrator_url: Url::parse("http://localhost").unwrap(), + sequencer_url: Url::parse("http://localhost").unwrap(), port: None, stake_table_capacity: 10, } @@ -638,7 +666,6 @@ mod test { async fn test_read_contract_state() -> Result<()> { setup_logging(); setup_backtrace(); - let anvil = Anvil::new().spawn(); let dummy_genesis = ParsedLightClientState::dummy_genesis(); let (_wallet, contract) = deploy_contract_for_test(&anvil, dummy_genesis.clone()).await?; diff --git a/hotshot-state-prover/src/snark.rs b/hotshot-state-prover/src/snark.rs index 2e166003f..1e9f5c1f1 100644 --- a/hotshot-state-prover/src/snark.rs +++ b/hotshot-state-prover/src/snark.rs @@ -11,7 +11,7 @@ use jf_plonk::{ proof_system::{PlonkKzgSnark, UniversalSNARK}, transcript::SolidityTranscript, }; -use jf_primitives::signatures::schnorr::Signature; +use jf_signature::schnorr::Signature; /// BLS verification key, base field and Schnorr verification key pub use hotshot_stake_table::vec_based::config::QCVerKey; @@ -113,16 +113,17 @@ mod tests { light_client::GenericLightClientState, traits::stake_table::{SnapshotVersion, StakeTableScheme}, }; + use jf_crhf::CRHF; use jf_plonk::{ proof_system::{PlonkKzgSnark, UniversalSNARK}, transcript::SolidityTranscript, }; - use jf_primitives::{ - crhf::{VariableLengthRescueCRHF, CRHF}, - errors::PrimitivesError, - signatures::{schnorr::Signature, SchnorrSignatureScheme, SignatureScheme}, - }; use jf_relation::Circuit; + use jf_rescue::crhf::VariableLengthRescueCRHF; + use jf_signature::{ + schnorr::{SchnorrSignatureScheme, Signature}, + SignatureScheme, + }; use jf_utils::test_rng; const ST_CAPACITY: usize = 20; @@ -132,7 +133,7 @@ mod tests { fn universal_setup_for_testing( max_degree: usize, rng: &mut R, - ) -> Result + ) -> anyhow::Result where R: RngCore + CryptoRng, { @@ -219,7 +220,7 @@ mod tests { let sigs = schnorr_keys .iter() .map(|(key, _)| SchnorrSignatureScheme::::sign(&(), key, state_msg, &mut prng)) - .collect::, PrimitivesError>>() + .collect::, _>>() .unwrap(); // bit vector with total weight 26 diff --git a/hotshot-state-prover/src/test_utils.rs b/hotshot-state-prover/src/test_utils.rs index 06688790d..6822ae7e9 100644 --- a/hotshot-state-prover/src/test_utils.rs +++ b/hotshot-state-prover/src/test_utils.rs @@ -3,14 +3,15 @@ use ark_std::rand::{CryptoRng, RngCore}; use ethers::types::U256; use hotshot_stake_table::vec_based::StakeTable; use hotshot_types::traits::stake_table::StakeTableScheme; -use jf_primitives::signatures::{ +use jf_signature::{ bls_over_bn254::{BLSOverBN254CurveSignatureScheme, VerKey as BLSVerKey}, - SchnorrSignatureScheme, SignatureScheme, + schnorr::SchnorrSignatureScheme, + SignatureScheme, }; type F = ark_ed_on_bn254::Fq; -type SchnorrVerKey = jf_primitives::signatures::schnorr::VerKey; -type SchnorrSignKey = jf_primitives::signatures::schnorr::SignKey; +type SchnorrVerKey = jf_signature::schnorr::VerKey; +type SchnorrSignKey = jf_signature::schnorr::SignKey; /// Helper function for test pub(crate) fn key_pairs_for_testing( diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..761b13dce --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +require('dotenv').config(); + +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + transform: {'^.+\\.ts?$': 'ts-jest'}, + testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], +}; + +// Custom configuration for specific tests diff --git a/justfile b/justfile index 4abe55ad4..8a6596c8f 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,9 @@ default: just --list +doc *args: + cargo doc --no-deps --document-private-items {{args}} + demo *args: docker compose up {{args}} @@ -30,6 +33,9 @@ test: cargo build --bin diff-test --release cargo test --release --all-features +clippy: + cargo clippy --workspace --all-features --all-targets -- -D warnings + # Helpful shortcuts for local development dev-orchestrator: target/release/orchestrator -p 8080 -n 1 diff --git a/package.json b/package.json new file mode 100644 index 000000000..9f7593f94 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "espresso-sequencer", + "version": "0.1.0", + "main": "index.js", + "repository": "https://github.com/EspressoSystems/espresso-sequencer.git", + "author": "Espresso Systems ", + "license": "UNLICENSED", + "devDependencies": { + "@types/jest": "^29.5.12", + "ts-jest": "^29.1.2", + "typescript": "^5.4.5" + }, + "dependencies": { + "@safe-global/api-kit": "^2.3.1", + "@safe-global/protocol-kit": "^3.1.0", + "dotenv": "^16.4.5", + "ethers": "^6.12.1", + "jest": "^29.7.0", + "ts-node": "^10.9.2" + } +} diff --git a/process-compose.yaml b/process-compose.yaml index 47a6e1832..67796668b 100644 --- a/process-compose.yaml +++ b/process-compose.yaml @@ -5,8 +5,11 @@ environment: - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL=http://localhost:$ESPRESSO_ORCHESTRATOR_PORT - ESPRESSO_SEQUENCER_URL=http://localhost:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_L1_PROVIDER=http://localhost:$ESPRESSO_SEQUENCER_L1_PORT + - ESPRESSO_SEQUENCER_GENESIS_FILE=data/genesis/demo.toml + - ESPRESSO_BUILDER_GENESIS_FILE=data/genesis/demo.toml - ESPRESSO_DEMO_L1_HTTP_PROVIDER=$ESPRESSO_SEQUENCER_L1_PROVIDER - ESPRESSO_STATE_RELAY_SERVER_URL=http://localhost:$ESPRESSO_STATE_RELAY_SERVER_PORT + - QUERY_SERVICE_URI=http://localhost:$ESPRESSO_SEQUENCER1_API_PORT/v0/ processes: # Cheating a bit here but since we don't usually have to debug go-ethereum # it's using the docker compose service which is a bit easier. @@ -21,22 +24,47 @@ processes: success_threshold: 1 failure_threshold: 20 - deploy-contracts: + deploy-sequencer-contracts: # The contract addresses are implicitly inherited from .env. We need to unset these or else the # script will think they're already deployed. - command: unset ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS && deploy --use-mock-contract - environment: - - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY=10 + command: unset ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS && deploy --only fee-contract depends_on: demo-l1-network: condition: process_healthy - orchestrator: + + deploy-prover-contracts: + # The contract addresses are implicitly inherited from .env. We need to unset these or else the + # script will think they're already deployed. + command: unset ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS && deploy --use-mock-contract --only hotshot,light-client + depends_on: + demo-l1-network: + condition: process_healthy + sequencer0: + condition: process_healthy + # Make sure this doesn't start until the other contracts have been deployed, since we use the same mnemonic. + deploy-sequencer-contracts: + condition: process_completed + + fund-builder: + command: espresso-bridge deposit + environment: + - L1_PROVIDER=http://localhost:$ESPRESSO_SEQUENCER_L1_PORT + - ESPRESSO_PROVIDER=http://localhost:$ESPRESSO_SEQUENCER1_API_PORT + - CONTRACT_ADDRESS=0xa15bb66138824a1c7167f5e85b957d04dd34e468 + - MNEMONIC=$ESPRESSO_BUILDER_ETH_MNEMONIC + - ACCOUNT_INDEX=$ESPRESSO_BUILDER_ETH_ACCOUNT_INDEX + - AMOUNT=1000000000000000000 + - CONFIRMATIONS=1 + depends_on: + deploy-sequencer-contracts: + condition: process_completed + sequencer1: condition: process_healthy orchestrator: command: orchestrator environment: - - ESPRESSO_ORCHESTRATOR_BUILDER_URL=http://localhost:$ESPRESSO_BUILDER_SERVER_PORT + - ESPRESSO_ORCHESTRATOR_BUILDER_URLS=http://localhost:$ESPRESSO_BUILDER_SERVER_PORT readiness_probe: http_get: scheme: http @@ -62,31 +90,32 @@ processes: command: state-prover -d environment: - MNEMONIC=$ESPRESSO_SEQUENCER_ETH_MNEMONIC - - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY=10 - - ESPRESSO_STATE_PROVER_UPDATE_INTERVAL=30s - RAYON_NUM_THREADS=$PROVER_RAYON_NUM_THREADS depends_on: - orchestrator: + sequencer0: condition: process_healthy state-relay-server: condition: process_healthy demo-l1-network: condition: process_healthy - deploy-contracts: + deploy-prover-contracts: condition: process_completed sequencer0: - command: sequencer -- http -- query -- catchup -- status -- submit -- hotshot-events + command: sequencer -- storage-sql -- http -- query -- catchup -- status -- submit -- hotshot-events -- config environment: - ESPRESSO_SEQUENCER_API_PORT=$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS=0.0.0.0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_0 - ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS=localhost:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_0 - ESPRESSO_SEQUENCER_API_PEERS=http://localhost:$ESPRESSO_SEQUENCER1_API_PORT - ESPRESSO_SEQUENCER_STATE_PEERS=http://localhost:$ESPRESSO_SEQUENCER1_API_PORT - - ESPRESSO_SEQUENCER_STORAGE_PATH=$ESPRESSO_BASE_STORAGE_PATH/seq0 + - ESPRESSO_SEQUENCER_POSTGRES_HOST=localhost + - ESPRESSO_SEQUENCER_POSTGRES_PORT=$ESPRESSO_SEQUENCER0_DB_PORT + - ESPRESSO_SEQUENCER_POSTGRES_USER=root + - ESPRESSO_SEQUENCER_POSTGRES_PASSWORD=password + - ESPRESSO_SEQUENCER_POSTGRES_DATABASE=sequencer - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_0 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_0 - - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY=10 - ESPRESSO_SEQUENCER_IS_DA=true depends_on: orchestrator: @@ -95,14 +124,16 @@ processes: condition: process_healthy state-relay-server: condition: process_healthy + sequencer-db-0: + condition: process_healthy broker_0: condition: process_healthy broker_1: condition: process_healthy marshal_0: condition: process_healthy - permissionless-builder: - condition: process_healthy + deploy-sequencer-contracts: + condition: process_completed readiness_probe: http_get: @@ -113,7 +144,7 @@ processes: failure_threshold: 100 sequencer1: - command: sequencer -- storage-sql -- http -- query -- catchup -- status -- state + command: sequencer -- storage-sql -- http -- query -- catchup -- status -- state -- explorer environment: - ESPRESSO_SEQUENCER_API_PORT=$ESPRESSO_SEQUENCER1_API_PORT - ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS=0.0.0.0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_1 @@ -121,12 +152,12 @@ processes: - ESPRESSO_SEQUENCER_API_PEERS=http://localhost:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_STATE_PEERS=http://localhost:$ESPRESSO_SEQUENCER2_API_PORT - ESPRESSO_SEQUENCER_POSTGRES_HOST=localhost - - ESPRESSO_SEQUENCER_POSTGRES_PORT=$ESPRESSO_SEQUENCER_DB_PORT + - ESPRESSO_SEQUENCER_POSTGRES_PORT=$ESPRESSO_SEQUENCER1_DB_PORT - ESPRESSO_SEQUENCER_POSTGRES_USER=root - ESPRESSO_SEQUENCER_POSTGRES_PASSWORD=password + - ESPRESSO_SEQUENCER_POSTGRES_DATABASE=sequencer - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_1 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_1 - - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY=10 - ESPRESSO_SEQUENCER_IS_DA=true depends_on: orchestrator: @@ -135,7 +166,7 @@ processes: condition: process_healthy state-relay-server: condition: process_healthy - sequencer-db: + sequencer-db-1: condition: process_healthy broker_0: condition: process_healthy @@ -143,8 +174,8 @@ processes: condition: process_healthy marshal_0: condition: process_healthy - permissionless-builder: - condition: process_healthy + deploy-sequencer-contracts: + condition: process_completed readiness_probe: http_get: scheme: http @@ -163,7 +194,6 @@ processes: - ESPRESSO_SEQUENCER_STORAGE_PATH=$ESPRESSO_BASE_STORAGE_PATH/seq2 - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_2 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_2 - - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY=10 - ESPRESSO_SEQUENCER_IS_DA=true depends_on: orchestrator: @@ -178,8 +208,8 @@ processes: condition: process_healthy marshal_0: condition: process_healthy - permissionless-builder: - condition: process_healthy + deploy-sequencer-contracts: + condition: process_completed readiness_probe: http_get: scheme: http @@ -198,7 +228,6 @@ processes: - ESPRESSO_SEQUENCER_STORAGE_PATH=$ESPRESSO_BASE_STORAGE_PATH/seq3 - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_3 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_3 - - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY=10 depends_on: orchestrator: condition: process_healthy @@ -212,8 +241,8 @@ processes: condition: process_healthy marshal_0: condition: process_healthy - permissionless-builder: - condition: process_healthy + deploy-sequencer-contracts: + condition: process_completed readiness_probe: http_get: scheme: http @@ -230,7 +259,6 @@ processes: - ESPRESSO_SEQUENCER_STORAGE_PATH=$ESPRESSO_BASE_STORAGE_PATH/seq4 - ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=$ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_4 - ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=$ESPRESSO_DEMO_SEQUENCER_STATE_PRIVATE_KEY_4 - - ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY=10 depends_on: orchestrator: condition: process_healthy @@ -244,8 +272,8 @@ processes: condition: process_healthy marshal_0: condition: process_healthy - permissionless-builder: - condition: process_healthy + deploy-sequencer-contracts: + condition: process_completed readiness_probe: http_get: scheme: http @@ -338,7 +366,7 @@ processes: condition: process_healthy demo-l1-network: condition: process_healthy - deploy-contracts: + deploy-prover-contracts: condition: process_completed readiness_probe: http_get: @@ -355,6 +383,10 @@ processes: depends_on: sequencer0: condition: process_healthy + # We don't directly depend on the builder, but transactions will not be finalized until it has + # started, so there is no point in starting before then. + permissionless-builder: + condition: process_healthy readiness_probe: http_get: scheme: http @@ -388,6 +420,10 @@ processes: environment: - ESPRESSO_SEQUENCER_STATE_PEERS=http://localhost:$ESPRESSO_SEQUENCER_API_PORT - ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_URL=http://localhost:$ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT + - ESPRESSO_BUILDER_GENESIS_FILE + depends_on: + fund-builder: + condition: process_completed readiness_probe: http_get: scheme: http @@ -398,6 +434,9 @@ processes: nasty-client: command: nasty-client + environment: + # Point the nasty client at sequencer1, the only one running the state API. + - ESPRESSO_SEQUENCER_URL=http://localhost:$ESPRESSO_SEQUENCER1_API_PORT depends_on: sequencer0: condition: process_healthy @@ -409,14 +448,32 @@ processes: path: /healthcheck failure_threshold: 100 - sequencer-db: - command: docker run -e POSTGRES_PASSWORD -e POSTGRES_USER -p $ESPRESSO_SEQUENCER_DB_PORT:5432 postgres + sequencer-db-0: + command: docker run -e POSTGRES_PASSWORD -e POSTGRES_USER -e POSTGRES_DB -p $ESPRESSO_SEQUENCER0_DB_PORT:5432 postgres + environment: + - POSTGRES_PASSWORD=password + - POSTGRES_USER=root + - POSTGRES_DB=sequencer + readiness_probe: + exec: + command: pg_isready -h localhost -p $ESPRESSO_SEQUENCER0_DB_PORT + initial_delay_seconds: 5 + period_seconds: 5 + timeout_seconds: 4 + # Postgres can be falsely "ready" once before running init scripts. + # See https://github.com/docker-library/postgres/issues/146 for discussion. + success_threshold: 2 + failure_threshold: 20 + + sequencer-db-1: + command: docker run -e POSTGRES_PASSWORD -e POSTGRES_USER -e POSTGRES_DB -p $ESPRESSO_SEQUENCER1_DB_PORT:5432 postgres environment: - POSTGRES_PASSWORD=password - POSTGRES_USER=root + - POSTGRES_DB=sequencer readiness_probe: exec: - command: pg_isready -h localhost -p $ESPRESSO_SEQUENCER_DB_PORT + command: pg_isready -h localhost -p $ESPRESSO_SEQUENCER1_DB_PORT initial_delay_seconds: 5 period_seconds: 5 timeout_seconds: 4 @@ -424,3 +481,9 @@ processes: # See https://github.com/docker-library/postgres/issues/146 for discussion. success_threshold: 2 failure_threshold: 20 + + block-explorer: + command: docker run --rm -p $ESPRESSO_BLOCK_EXPLORER_PORT:3000 -e QUERY_SERVICE_URI ghcr.io/espressosystems/espresso-block-explorer:main + depends_on: + sequencer1: + condition: process_healthy diff --git a/scripts/build-docker-images b/scripts/build-docker-images index 1a256824a..2b31d20d4 100755 --- a/scripts/build-docker-images +++ b/scripts/build-docker-images @@ -7,7 +7,6 @@ nix develop .#armCrossShell --ignore-environment --command cargo build --release # The rest of the script doesn't run in a nix shell but we need to know where # the binaries are. CARGO_TARGET_DIR="./target/nix" -CONTRACTS_DIR="./contracts" # Copy binaries to a temporary directory. WORKDIR=$(mktemp -d -t espresso-docker-build-XXXXXXXX) @@ -18,6 +17,10 @@ cleanup(){ rm -rfv ${WORKDIR} } +# Copy data files to Docker context. +mkdir -p ${WORKDIR}/data +cp -rv data/genesis ${WORKDIR}/data/ + for ARCH in "amd64" "arm64"; do case "$ARCH" in amd64) @@ -32,11 +35,18 @@ for ARCH in "amd64" "arm64"; do ;; esac mkdir -p ${WORKDIR}/target/$ARCH/release - for binary in "orchestrator" "cdn-broker" "cdn-marshal" "cdn-whitelist" "sequencer" "commitment-task" "submit-transactions" "reset-storage" "state-relay-server" "state-prover" "deploy" "keygen" "permissionless-builder" "nasty-client" "pub-key"; do + for binary in "orchestrator" "cdn-broker" "cdn-marshal" "cdn-whitelist" "sequencer" "commitment-task" "submit-transactions" "reset-storage" "state-relay-server" "state-prover" "deploy" "keygen" "permissionless-builder" "nasty-client" "pub-key" "espresso-bridge" "espresso-dev-node"; do cp -v "${CARGO_TARGET_DIR}/${TARGET}/release/$binary" ${WORKDIR}/target/$ARCH/release done + + # Download the latest foundry binary and extract anvil for the dev-node docker image. + curl -L https://github.com/foundry-rs/foundry/releases/download/nightly/foundry_nightly_linux_${ARCH}.tar.gz -o ${WORKDIR}/foundry.tar.gz + tar -xzvf ${WORKDIR}/foundry.tar.gz -C ${WORKDIR}/target/$ARCH/release anvil done +# Copy the dev-node launch script +cp -v scripts/launch-dev-node-with-postgres ${WORKDIR} + export DOCKER_BUILDKIT=1 docker build -t ghcr.io/espressosystems/espresso-sequencer/orchestrator:main -f docker/orchestrator.Dockerfile ${WORKDIR} docker build -t ghcr.io/espressosystems/espresso-sequencer/cdn-broker:main -f docker/cdn-broker.Dockerfile ${WORKDIR} @@ -50,3 +60,5 @@ docker build -t ghcr.io/espressosystems/espresso-sequencer/submit-transactions:m docker build -t ghcr.io/espressosystems/espresso-sequencer/deploy:main -f docker/deploy.Dockerfile ${WORKDIR} docker build -t ghcr.io/espressosystems/espresso-sequencer/builder:main -f docker/permissionless-builder.Dockerfile ${WORKDIR} docker build -t ghcr.io/espressosystems/espresso-sequencer/nasty-client:main -f docker/nasty-client.Dockerfile ${WORKDIR} +docker build -t ghcr.io/espressosystems/espresso-sequencer/espresso-dev-node:main -f docker/espresso-dev-node.Dockerfile ${WORKDIR} +docker build -t ghcr.io/espressosystems/espresso-sequencer/bridge:main -f docker/espresso-bridge.Dockerfile ${WORKDIR} diff --git a/scripts/build-docker-images-native b/scripts/build-docker-images-native index 61f5fb226..10f659681 100755 --- a/scripts/build-docker-images-native +++ b/scripts/build-docker-images-native @@ -82,8 +82,12 @@ cleanup(){ rm -rfv "${WORKDIR}" } +# Copy data files to Docker context. +mkdir -p ${WORKDIR}/data +cp -rv data/genesis ${WORKDIR}/data/ + mkdir -p "${WORKDIR}/target/$ARCH/release" -for binary in "orchestrator" "cdn-broker" "cdn-marshal" "cdn-whitelist" "sequencer" "commitment-task" "submit-transactions" "reset-storage" "state-relay-server" "state-prover" "deploy" "keygen" "permissionless-builder" "nasty-client" "pub-key"; do +for binary in "orchestrator" "cdn-broker" "cdn-marshal" "cdn-whitelist" "sequencer" "commitment-task" "submit-transactions" "reset-storage" "state-relay-server" "state-prover" "deploy" "keygen" "permissionless-builder" "nasty-client" "pub-key" "espresso-bridge" "espresso-dev-node"; do cp -v "${CARGO_TARGET_DIR}/release/$binary" "${WORKDIR}/target/$ARCH/release" # Patch the interpreter for running without nix inside the ubuntu based docker image. if [ $KERNEL == "linux" ]; then @@ -91,6 +95,14 @@ for binary in "orchestrator" "cdn-broker" "cdn-marshal" "cdn-whitelist" "sequenc fi done +# Copy the dev-node launch script +cp -v scripts/launch-dev-node-with-postgres ${WORKDIR} + +# Download the latest foundry binary and extract anvil for the dev-node docker image. +curl -L https://github.com/foundry-rs/foundry/releases/download/nightly/foundry_nightly_linux_${ARCH}.tar.gz -o ${WORKDIR}/foundry.tar.gz +tar -xzvf ${WORKDIR}/foundry.tar.gz -C ${WORKDIR}/target/$ARCH/release anvil + + export DOCKER_BUILDKIT=1 docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/orchestrator:main -f docker/orchestrator.Dockerfile ${WORKDIR} docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/cdn-broker:main -f docker/cdn-broker.Dockerfile ${WORKDIR} @@ -104,3 +116,5 @@ docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/ docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/deploy:main -f docker/deploy.Dockerfile ${WORKDIR} docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/builder:main -f docker/permissionless-builder.Dockerfile ${WORKDIR} docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/nasty-client:main -f docker/nasty-client.Dockerfile ${WORKDIR} +docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/espresso-dev-node:main -f docker/espresso-dev-node.Dockerfile ${WORKDIR} +docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/bridge:main -f docker/espresso-bridge.Dockerfile ${WORKDIR} diff --git a/scripts/demo-native b/scripts/demo-native index a44570a63..56493c7ff 100755 --- a/scripts/demo-native +++ b/scripts/demo-native @@ -1,7 +1,14 @@ #!/usr/bin/env bash set -euo pipefail -export PATH=$PATH:./target/release +# If we aren't in a nix shell (where this handled in flake.nix) add the target +# directory to the path so that the binaries are found by process-compose. +if [ -z "${IN_NIX_SHELL-}" ]; then + REPO_ROOT="$(dirname "$(dirname "$(readlink -fm "$0")")")" + # Default to CARGO_TARGET_DIR if set, otherwise use the default target directory. + TARGET_DIR="${CARGO_TARGET_DIR:-${REPO_ROOT}/target}" + export "PATH=${TARGET_DIR}/release:$PATH" +fi ESPRESSO_BASE_STORAGE_PATH=$(mktemp -d -t espresso-XXXXXXXX) export ESPRESSO_BASE_STORAGE_PATH diff --git a/scripts/fmt-pc-logs b/scripts/fmt-pc-logs new file mode 100755 index 000000000..4f0f14073 --- /dev/null +++ b/scripts/fmt-pc-logs @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# +# This script takes the logs output by process-compose (formatted as JSON) and writes them in an +# easier-to-read plaintext format with colors. +# +# Usage (assuming `log_location: /tmp/pc.log` in `process-compose.yaml`: +# +# cat /tmp/pc.log | scripts/fmt-pc-logs > /tmp/formatted.log +# + +import fileinput +import json + +def main(): + for line in fileinput.input(encoding="utf-8"): + obj = json.loads(line) + if 'message' in obj: + print(obj['process'] + '| ' + obj['message']) + +if __name__ == '__main__': + main() diff --git a/scripts/launch-dev-node-with-postgres b/scripts/launch-dev-node-with-postgres new file mode 100644 index 000000000..1aef1e556 --- /dev/null +++ b/scripts/launch-dev-node-with-postgres @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euxo pipefail + +export ESPRESSO_SEQUENCER_POSTGRES_HOST=${ESPRESSO_SEQUENCER_POSTGRES_HOST:-localhost} +export ESPRESSO_SEQUENCER_POSTGRES_PORT=${ESPRESSO_SEQUENCER_POSTGRES_PORT:-5432} +export ESPRESSO_SEQUENCER_POSTGRES_USER=${ESPRESSO_SEQUENCER_POSTGRES_USER:-root} +export ESPRESSO_SEQUENCER_POSTGRES_PASSWORD=${ESPRESSO_SEQUENCER_POSTGRES_PASSWORD:-password} + +export POSTGRES_USER=$ESPRESSO_SEQUENCER_POSTGRES_USER +export POSTGRES_PASSWORD=$ESPRESSO_SEQUENCER_POSTGRES_PASSWORD + +export RUST_LOG=${RUST_LOG:-info} + +# Start postgres in the background +docker-entrypoint.sh postgres & + +# Wait (twice) for postgres to be ready +# Postgres can be falsely "ready" once before running init scripts. +until pg_isready && sleep 1 && pg_isready; do + echo "Waiting for postgres..." + sleep 1 +done + +# Start the dev node +espresso-dev-node diff --git a/scripts/smoke-test-demo b/scripts/smoke-test-demo index 046540dab..725e0e891 100755 --- a/scripts/smoke-test-demo +++ b/scripts/smoke-test-demo @@ -1,32 +1,71 @@ #!/usr/bin/env bash +set -eE -set -e +# Duplicate some debugging information to stdout in case the process exits +trap 'error_handler "${LINENO}" "$BASH_COMMAND"' ERR INT +error_handler() { + local lineno=$1 + local msg=$2 + echo "Failed at line $lineno: msg: $msg" +} # load env vars in .env set -a; source .env; set +a; -SEQUENCER_API=http://localhost:$ESPRESSO_SEQUENCER_API_PORT +L1_PROVIDER=http://localhost:$ESPRESSO_SEQUENCER_L1_PORT +SEQUENCER_API=http://localhost:$ESPRESSO_SEQUENCER1_API_PORT LOAD_GENERATOR=http://localhost:$ESPRESSO_SUBMIT_TRANSACTIONS_PRIVATE_PORT SEQUENCER_BLOCKS_TIMEOUT=120 # usage: wait_for timeout_in_secs name URL function wait_for() { - timeout=$1 - what=$2 - url=$3 - start=$SECONDS - elapsed=0 - echo "Checking if $what @ $url is available, timeout in $timeout seconds" - while ! curl -sL --fail "$url" > /dev/null; do - elapsed=$((SECONDS - start)) - if [[ $elapsed -gt $timeout ]]; then - echo "Timeout waiting for $what @ $url" - exit 1 - fi - echo "Waiting for $what @ $url, $elapsed of $timeout seconds elapsed" - sleep 1 - done - echo "Endpoint $what @ $url is Ok, after $elapsed seconds" + timeout=$1 + what=$2 + url=$3 + start=$SECONDS + elapsed=0 + echo "Checking if $what @ $url is available, timeout in $timeout seconds" + while ! curl -sL --fail "$url" > /dev/null; do + elapsed=$((SECONDS - start)) + if [[ $elapsed -gt $timeout ]]; then + echo "Timeout waiting for $what @ $url" + exit 1 + fi + echo "Waiting for $what @ $url, $elapsed of $timeout seconds elapsed" + sleep 1 + done + echo "Endpoint $what @ $url is Ok, after $elapsed seconds" +} + +# usage: get_balance
+function get_balance() { + if which espresso-bridge > /dev/null 2>&1 ; then + # If the espresso-bridge program is built locally, use it. + ( + unset MNEMONIC + RUST_LOG=off espresso-bridge balance -e $SEQUENCER_API -a $1 -b $2 + ) + else + # Otherwise, use Docker. + docker run --network="host" -e RUST_LOG=off ghcr.io/espressosystems/espresso-sequencer/bridge:main \ + bridge balance -e $SEQUENCER_API -a $1 -b $2 + fi +} + +function last_light_client_update() { + curl -s "$L1_PROVIDER" \ + -H "Content-Type: application/json" \ + -d '{ + "method": "eth_getLogs", + "params": [{ + "fromBlock": "earliest", + "address": "'"$ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS"'" + }], + "id": 1, + "jsonrpc": "2.0" + }' \ + | jq '.result | .[-1].blockNumber // "0x0"' \ + | xargs printf "%d\n" # Convert from hex to decimal } # Wait for the load generator to start. @@ -34,12 +73,21 @@ wait_for 300 "demo load generator" "$LOAD_GENERATOR/healthcheck" # Get the block height and number of transactions, wait some time, and check that these numbers have # both increased. -block_height=`curl -sL $SEQUENCER_API/node/block-height` -num_tx=`curl -sL $SEQUENCER_API/node/transactions/count` +block_height=$(curl -sL $SEQUENCER_API/node/block-height) +num_tx=$(curl -sL $SEQUENCER_API/node/transactions/count) +# Get the balance of the builder and fee recipient accounts. The former should decrease over time +# while the latter should increase. +builder_balance=$(get_balance 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f $block_height) +recipient_balance=$(get_balance 0x0000000000000000000000000000000000000000 $block_height) +# Get the last L1 block where the light client was updated. This should increase over time. +light_client_updated=$(last_light_client_update) echo "Initial state:" echo " block_height: $block_height" echo " transactions: $num_tx" +echo " builder_balance: $builder_balance" +echo " recipient_balance: $recipient_balance" +echo " light_client_updated: $light_client_updated" # Bash magic to get the current time in seconds since start of this shell START=$SECONDS @@ -47,31 +95,56 @@ START=$SECONDS # Check blocks and transactions are increasing # Every second until timeout after $TIMEOUT seconds while true; do - new_block_height=`curl -sL $SEQUENCER_API/node/block-height` - new_num_tx=`curl -sL $SEQUENCER_API/node/transactions/count` - if [[ $new_block_height -gt $block_height && $new_num_tx -gt $num_tx ]]; then - echo "Final state:" - echo " block_height: $new_block_height" - echo " transactions: $new_num_tx" - echo "Block height and transaction count are increasing. Great!" - break - fi - sleep 1 - if [[ $((SECONDS - START)) -gt $SEQUENCER_BLOCKS_TIMEOUT ]]; then - echo "Timeout waiting for block height and transaction count to increase" - echo "Final state:" - echo " block_height: $new_block_height" - echo " transactions: $new_num_tx" - - if ! [[ $new_block_height -gt $block_height ]]; then - echo "Block height is not increasing!" - fi - if ! [[ $new_num_tx -gt $num_tx ]]; then - echo "Transaction count is not increasing!" - fi - - exit 1 - fi + new_block_height=`curl -sL $SEQUENCER_API/node/block-height` + new_num_tx=`curl -sL $SEQUENCER_API/node/transactions/count` + new_light_client_updated=`last_light_client_update` + if [[ $new_block_height -gt $block_height && $new_num_tx -gt $num_tx && $new_light_client_updated -gt $light_client_updated ]]; then + new_builder_balance=`get_balance 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f $new_block_height` + new_recipient_balance=`get_balance 0x0000000000000000000000000000000000000000 $new_block_height` + + echo "Final state:" + echo " block_height: $new_block_height" + echo " transactions: $new_num_tx" + echo " builder_balance: $new_builder_balance" + echo " recipient_balance: $new_recipient_balance" + echo " light_client_updated: $new_light_client_updated" + echo "Block height, transaction count, and light client updates are increasing. Great!" + + if ! [[ $new_recipient_balance -gt $recipient_balance ]]; then + echo "Fee recipient balance is not increasing!" + exit 1 + fi + if ! [[ $new_builder_balance -lt $builder_balance ]]; then + echo "Builder balance is not decreasing!" + exit 1 + fi + if [[ $((builder_balance + recipient_balance)) != $((new_builder_balance + new_recipient_balance)) ]]; then + echo "Balance not conserved!" + exit 1 + fi + + break + fi + sleep 1 + if [[ $((SECONDS - START)) -gt $SEQUENCER_BLOCKS_TIMEOUT ]]; then + echo "Timeout waiting for block height, transaction count, and light client updates to increase" + echo "Final state:" + echo " block_height: $new_block_height" + echo " transactions: $new_num_tx" + echo " light_client_updated: $new_light_client_updated" + + if ! [[ $new_block_height -gt $block_height ]]; then + echo "Block height is not increasing!" + fi + if ! [[ $new_num_tx -gt $num_tx ]]; then + echo "Transaction count is not increasing!" + fi + if ! [[ $new_light_client_updated -gt $light_client_updated ]]; then + echo "Light client is not being updated!" + fi + + exit 1 + fi done diff --git a/sequencer/Cargo.toml b/sequencer/Cargo.toml index d414ee34f..658cc7de2 100644 --- a/sequencer/Cargo.toml +++ b/sequencer/Cargo.toml @@ -6,15 +6,29 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +default = ["libp2p"] testing = ["hotshot-testing"] libp2p = [] +[[bin]] +name = "espresso-dev-node" +required-features = ["testing"] + [dev-dependencies] +escargot = "0.5.10" espresso-macros = { git = "https://github.com/EspressoSystems/espresso-macros.git", tag = "0.1.0" } +fluent-asserter = "0.1.9" hotshot-query-service = { workspace = true, features = ["testing"] } +hotshot-testing = { workspace = true } +pretty_assertions = { workspace = true } rand = "0.8.5" +reqwest = { workspace = true } tempfile = "3.9.0" +[build-dependencies] +anyhow = { workspace = true } +vergen = { workspace = true } + [dependencies] anyhow = { workspace = true } ark-bls12-381 = { workspace = true } @@ -29,7 +43,7 @@ async-std = { workspace = true } async-trait = { workspace = true } base64 = { workspace = true } base64-bytes = { workspace = true } -bincode = "1.3.3" +bincode = { workspace = true } blake3 = { workspace = true } bytesize = { workspace = true } @@ -48,8 +62,10 @@ es-version = { workspace = true } ethers = { workspace = true } ethers-contract-derive = "2.0.10" futures = { workspace = true } +paste = "1.0" hotshot = { workspace = true } +hotshot-contract-adapter = { workspace = true } hotshot-events-service = { workspace = true } hotshot-orchestrator = { workspace = true } hotshot-query-service = { workspace = true } @@ -60,14 +76,18 @@ hotshot-task = { workspace = true } # Dependencies for feature `testing` hotshot-testing = { workspace = true, optional = true } hotshot-types = { workspace = true } -hotshot-web-server = { workspace = true } include_dir = "0.7" itertools = { workspace = true } +jf-crhf = { workspace = true } +jf-merkle-tree = { workspace = true } +jf-rescue = { workspace = true } -jf-primitives = { workspace = true } +jf-signature = { workspace = true, features = ["bls", "schnorr"] } jf-utils = { workspace = true } # TODO temporary: used only for test_rng() -lazy_static = "1.4" +jf-vid = { workspace = true } +libp2p = { workspace = true } num-traits = "0.2.18" +num_enum = "0.7" portpicker = { workspace = true } rand = "0.8.5" rand_chacha = { workspace = true } @@ -77,11 +97,13 @@ serde = { workspace = true } serde_json = "^1.0.113" sha2 = "0.10" # TODO temporary, used only for VID, should be set in hotshot snafu = { workspace = true } +static_assertions = "1" strum = { workspace = true } surf-disco = { workspace = true } tagged-base64 = { workspace = true } +thiserror = { workspace = true } tide-disco = { workspace = true } -time = "0.3" +time = { workspace = true } tokio-postgres = { version = "0.7", default-features = false, features = [ # disabling the default features removes dependence on the tokio runtime "with-serde_json-1", ] } @@ -89,12 +111,12 @@ toml = { workspace = true } tracing = { workspace = true } tracing-subscriber = "0.3.18" trait-set = "0.3.0" -trait-variant = { workspace = true } typenum = { version = "1.15.0", default-features = false, features = [ "no_std", ] } url = { workspace = true } vbs = { workspace = true } +vec1 = { workspace = true } zeroize = { workspace = true } [package.metadata.cargo-udeps.ignore] diff --git a/sequencer/api/catchup.toml b/sequencer/api/catchup.toml index 27afbf1de..af7d942ba 100644 --- a/sequencer/api/catchup.toml +++ b/sequencer/api/catchup.toml @@ -1,18 +1,23 @@ [route.account] -PATH = ["/:view/account/:address", "/account/:address"] +PATH = ["/:height/:view/account/:address"] +":height" = "Integer" ":view" = "Integer" ":address" = "Literal" DOC = """ Get the fee account balance for `address`. This endpoint can be used to catch up to the current state from a recent state by fetching the -balance (with proof) at the given `:view` number. It can also be used to query the latest finalized -state. +balance (with proof) at the given block `:height` and `:view` number. `:height` and `:view` _must_ +correspond! `:height` is provided to simplify lookups for backends where data is not indexed by +view. -Returns the account balance and a Merkle proof relative to the fee state root at the requested view. -If there is no entry for this account in the requested fee state (note: this is distinct from the -server _not knowing_ the entry for this account), the returned balance is 0 and the proof is a -Merkle _non-membership_ proof. +This endpoint is intended to be used for catchup, so `:view` should be no older than the last +decided view. + +Returns the account balance and a Merkle proof relative to the fee state root at the requested +height and view. If there is no entry for this account in the requested fee state (note: this is +distinct from the server _not knowing_ the entry for this account), the returned balance is 0 and +the proof is a Merkle _non-membership_ proof. ``` { @@ -23,14 +28,31 @@ Merkle _non-membership_ proof. """ [route.blocks] -PATH = ["/:view/blocks", "/blocks"] +PATH = ["/:height/:view/blocks"] +":height" = "Integer" ":view" = "Integer" DOC = """ Get the blocks Merkle tree frontier. This endpoint can be used to catch up to the current state from a recent state by fetching the -frontier at the given `:view` number. It can also be used to query the latest finalized state. +frontier at the given block `:height` and `:view` number. `:height` and `:view` _must_ correspond! +`:height` is provided to simplify lookups for backends where data is not indexed by view. + +This endpoint is intended to be used for catchup, so `:view` should be no older than the last +decided view. Returns the blocks Merkle tree frontier -- the path to the most recently appended leaf, relative to -root node at the requested view. +root node at the requested block height and view. """ + +[route.chainconfig] +PATH = ["/chain-config/:commitment"] +":commitment" = "TaggedBase64" +DOC = """ + +This endpoint retrieves the chain config from a peer that matches the specified `:commitment`. +This is only called when the state does not have full chain config which is different from the genesis one. +This can happen if the node missed a protocol upgrade. + +Returns the chain config -- this includes parameters such as `max_block_size`, `chain_id`, `base_fee`, and `fee_recipient`. +""" \ No newline at end of file diff --git a/sequencer/api/config.toml b/sequencer/api/config.toml new file mode 100644 index 000000000..c2f4d40ac --- /dev/null +++ b/sequencer/api/config.toml @@ -0,0 +1,9 @@ +[route.hotshot] +PATH = ["/hotshot"] +METHOD = "GET" +DOC = "Get the Hotshot configuration for the current node." + +[route.env] +PATH = ["/env"] +METHOD = "GET" +DOC = "Get all ESPRESSO environment variables set for the current node." \ No newline at end of file diff --git a/sequencer/api/migrations/V14__state_tables.sql b/sequencer/api/migrations/V14__state_tables.sql index c3a6c51f3..d5704006c 100644 --- a/sequencer/api/migrations/V14__state_tables.sql +++ b/sequencer/api/migrations/V14__state_tables.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS hash ( ); CREATE TABLE fee_merkle_tree ( - pos LTREE NOT NULL, + path INTEGER[] NOT NULL, created BIGINT NOT NULL, hash_id INT NOT NULL REFERENCES hash (id), children INT[], @@ -15,12 +15,12 @@ CREATE TABLE fee_merkle_tree ( ALTER TABLE fee_merkle_tree ADD - CONSTRAINT fee_merkle_tree_pk PRIMARY KEY (pos, created); + CONSTRAINT fee_merkle_tree_pk PRIMARY KEY (path, created); -CREATE INDEX fee_merkle_tree_path ON fee_merkle_tree USING GIST (pos); +CREATE INDEX fee_merkle_tree_created ON fee_merkle_tree (created); CREATE TABLE block_merkle_tree ( - pos LTREE NOT NULL, + path INTEGER[] NOT NULL, created BIGINT NOT NULL, hash_id INT NOT NULL REFERENCES hash (id), children INT[], @@ -32,6 +32,6 @@ CREATE TABLE block_merkle_tree ( ALTER TABLE block_merkle_tree ADD - CONSTRAINT block_merkle_tree_pk PRIMARY KEY (pos, created); + CONSTRAINT block_merkle_tree_pk PRIMARY KEY (path, created); -CREATE INDEX block_merkle_tree_path ON block_merkle_tree USING GIST (pos); +CREATE INDEX block_merkle_tree_created ON block_merkle_tree (created); diff --git a/sequencer/api/migrations/V15__undecided_state.sql b/sequencer/api/migrations/V15__undecided_state.sql new file mode 100644 index 000000000..48e67f4cb --- /dev/null +++ b/sequencer/api/migrations/V15__undecided_state.sql @@ -0,0 +1,8 @@ +CREATE TABLE undecided_state ( + -- The ID is always set to 0. Setting it explicitly allows us to enforce with every insert or + -- update that there is only a single entry in this table: the latest known state. + id INT PRIMARY KEY, + + leaves BYTEA NOT NULL, + state BYTEA NOT NULL +); diff --git a/sequencer/api/migrations/V16__merkle_root_columns.sql b/sequencer/api/migrations/V16__merkle_root_columns.sql new file mode 100644 index 000000000..11735f1ba --- /dev/null +++ b/sequencer/api/migrations/V16__merkle_root_columns.sql @@ -0,0 +1,10 @@ +ALTER TABLE header +ADD column block_merkle_tree_root text +GENERATED ALWAYS AS (data->>'block_merkle_tree_root') STORED NOT NULL; + +ALTER TABLE header +ADD column fee_merkle_tree_root text +GENERATED ALWAYS as (data->>'fee_merkle_tree_root') STORED NOT NULL; + +CREATE INDEX header_block_merkle_tree_root_idx ON header (block_merkle_tree_root); +CREATE INDEX header_fee_merkle_tree_root_idx ON header (fee_merkle_tree_root); \ No newline at end of file diff --git a/sequencer/api/migrations/V31__drop_merklized_state_created_index.sql b/sequencer/api/migrations/V31__drop_merklized_state_created_index.sql new file mode 100644 index 000000000..a25664303 --- /dev/null +++ b/sequencer/api/migrations/V31__drop_merklized_state_created_index.sql @@ -0,0 +1,13 @@ +-- Indexes on created block height are not strictly necessary for Merklized state queries. In most +-- cases, we want the queries to use the multi-column index on (path, created), which allows us to +-- seek directly to the desired path down the tree and then take the latest version of that path in +-- a single B-tree traversal. +-- +-- Occasionally, it is marginally faster to use the created index, such as when a Merkle node was +-- modified very recently, and you don't have to scan back very far in the created index before +-- finding a version of that node. However, it is sometimes *much* slower to use `created` over the +-- multi-column index, such as when a node hasn't had a new version in a very long time. For reasons +-- that are not well understood, having these indexes causes the query planner to sometimes use them +-- in the extremely slow cases, but dropping them means we always use the multi-column index. +DROP INDEX fee_merkle_tree_created; +DROP INDEX block_merkle_tree_created; diff --git a/sequencer/api/migrations/V32__saved_proposals.sql b/sequencer/api/migrations/V32__saved_proposals.sql new file mode 100644 index 000000000..7c782f99d --- /dev/null +++ b/sequencer/api/migrations/V32__saved_proposals.sql @@ -0,0 +1,4 @@ +CREATE TABLE quorum_proposals ( + view BIGINT PRIMARY KEY, + data BYTEA +); \ No newline at end of file diff --git a/sequencer/api/migrations/V33__chain_config_table.sql b/sequencer/api/migrations/V33__chain_config_table.sql new file mode 100644 index 000000000..f41825ac0 --- /dev/null +++ b/sequencer/api/migrations/V33__chain_config_table.sql @@ -0,0 +1,4 @@ +CREATE TABLE chain_config ( + commitment VARCHAR PRIMARY KEY, + data BYTEA NOT NULL +); diff --git a/sequencer/api/public-env-vars.toml b/sequencer/api/public-env-vars.toml new file mode 100644 index 000000000..e37fcb1d6 --- /dev/null +++ b/sequencer/api/public-env-vars.toml @@ -0,0 +1,107 @@ +# TOML file for the `/env` route of Config endpoint +# This file is used when the Config module is enabled. +# It specifies a list of environment variable names (`variables`) +# that will be exposed by the `/env` route. + + +variables = [ + "ADDRESS", + "AMOUNT", + "BLOCK", + "CONFIRMATIONS", + "CONTRACT_ADDRESS", + "ESPRESSO_CDN_BROKER_CA_CERT_PATH", + "ESPRESSO_CDN_BROKER_DISCOVERY_ENDPOINT", + "ESPRESSO_CDN_BROKER_METRICS_BIND_ENDPOINT", + "ESPRESSO_CDN_BROKER_PUBLIC_ADVERTISE_ENDPOINT", + "ESPRESSO_CDN_BROKER_PUBLIC_BIND_ENDPOINT", + "ESPRESSO_CDN_MARSHAL_BIND_PORT", + "ESPRESSO_CDN_MARSHAL_CA_CERT_PATH", + "ESPRESSO_CDN_MARSHAL_DISCOVERY_ENDPOINT", + "ESPRESSO_CDN_MARSHAL_METRICS_BIND_ENDPOINT", + "ESPRESSO_CDN_WHITELIST_DISCOVERY_ENDPOINT", + "ESPRESSO_COMMITMENT_TASK_DELAY", + "ESPRESSO_COMMITMENT_TASK_PORT", + "ESPRESSO_COMMITMENT_TASK_REQUEST_TIMEOUT", + "ESPRESSO_DEPLOYER_OUT_PATH", + "ESPRESSO_NASTY_CLIENT_HTTP_TIMEOUT_ERROR", + "ESPRESSO_NASTY_CLIENT_HTTP_TIMEOUT_WARNING", + "ESPRESSO_NASTY_CLIENT_MAX_BLOCKING_POLLS", + "ESPRESSO_NASTY_CLIENT_MAX_OPEN_STREAMS", + "ESPRESSO_NASTY_CLIENT_MAX_RETRIES", + "ESPRESSO_NASTY_CLIENT_MIN_RETRIES", + "ESPRESSO_NASTY_CLIENT_PORT", + "ESPRESSO_NASTY_CLIENT_RETRY_DELAY", + "ESPRESSO_NASTY_CLIENT_WEB_SOCKET_TIMEOUT", + "ESPRESSO_NASTY_CLIENT_WEIGHT_CLOSE_STREAM", + "ESPRESSO_NASTY_CLIENT_WEIGHT_OPEN_STREAM", + "ESPRESSO_NASTY_CLIENT_WEIGHT_POLL_STREAM", + "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY", + "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_BLOCK_STATE", + "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_FEE_STATE", + "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_NAMESPACE", + "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_WINDOW", + "ESPRESSO_ORCHESTRATOR_BUILDER_TIMEOUT", + "ESPRESSO_ORCHESTRATOR_BUILDER_URLS", + "ESPRESSO_ORCHESTRATOR_LIBP2P_MESH_N", + "ESPRESSO_ORCHESTRATOR_NEXT_VIEW_TIMEOUT", + "ESPRESSO_ORCHESTRATOR_NUM_NODES", + "ESPRESSO_ORCHESTRATOR_PORT", + "ESPRESSO_ORCHESTRATOR_ROUND_START_DELAY", + "ESPRESSO_ORCHESTRATOR_START_DELAY", + "ESPRESSO_ORCHESTRATOR_START_THRESHOLD", + "ESPRESSO_ORCHESTRATOR_TIMEOUT_RATIO", + "ESPRESSO_PROVIDER", + "ESPRESSO_SEQUENCER_ACTIVE_FETCH_DELAY", + "ESPRESSO_SEQUENCER_API_PEERS", + "ESPRESSO_SEQUENCER_API_PORT", + "ESPRESSO_SEQUENCER_ARCHIVE", + "ESPRESSO_SEQUENCER_CATCHUP_BACKOFF_FACTOR", + "ESPRESSO_SEQUENCER_CATCHUP_BACKOFF_JITTER", + "ESPRESSO_SEQUENCER_CATCHUP_BASE_RETRY_DELAY", + "ESPRESSO_SEQUENCER_CATCHUP_MAX_RETRY_DELAY", + "ESPRESSO_SEQUENCER_CDN_ENDPOINT", + "ESPRESSO_SEQUENCER_CHUNK_FETCH_DELAY", + "ESPRESSO_SEQUENCER_FETCH_RATE_LIMIT", + "ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS", + "ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT", + "ESPRESSO_SEQUENCER_IS_DA", + "ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE", + "ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS", + "ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS", + "ESPRESSO_SEQUENCER_MAX_CONNECTIONS", + "ESPRESSO_SEQUENCER_ORCHESTRATOR_URL", + "ESPRESSO_SEQUENCER_POSTGRES_DATABASE", + "ESPRESSO_SEQUENCER_POSTGRES_HOST", + "ESPRESSO_SEQUENCER_POSTGRES_PORT", + "ESPRESSO_SEQUENCER_POSTGRES_PRUNE", + "ESPRESSO_SEQUENCER_POSTGRES_USE_TLS", + "ESPRESSO_SEQUENCER_POSTGRES_USER", + "ESPRESSO_SEQUENCER_PRUNER_BATCH_SIZE", + "ESPRESSO_SEQUENCER_PRUNER_INTERVAL", + "ESPRESSO_SEQUENCER_PRUNER_MAX_USAGE", + "ESPRESSO_SEQUENCER_PRUNER_MINIMUM_RETENTION", + "ESPRESSO_SEQUENCER_PRUNER_PRUNING_THRESHOLD", + "ESPRESSO_SEQUENCER_PRUNER_TARGET_RETENTION", + "ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY", + "ESPRESSO_SEQUENCER_STATE_PEERS", + "ESPRESSO_SEQUENCER_STORAGE_PATH", + "ESPRESSO_SEQUENCER_STORE_UNDECIDED_STATE", + "ESPRESSO_SEQUENCER_URL", + "ESPRESSO_STATE_RELAY_SERVER_URL", + "ESPRESSO_SUBMIT_TRANSACTIONS_CHANNEL_BOUND", + "ESPRESSO_SUBMIT_TRANSACTIONS_DELAY", + "ESPRESSO_SUBMIT_TRANSACTIONS_JOBS", + "ESPRESSO_SUBMIT_TRANSACTIONS_MAX_BATCH_SIZE", + "ESPRESSO_SUBMIT_TRANSACTIONS_MAX_NAMESPACE", + "ESPRESSO_SUBMIT_TRANSACTIONS_MAX_SIZE", + "ESPRESSO_SUBMIT_TRANSACTIONS_MIN_BATCH_SIZE", + "ESPRESSO_SUBMIT_TRANSACTIONS_MIN_NAMESPACE", + "ESPRESSO_SUBMIT_TRANSACTIONS_MIN_SIZE", + "ESPRESSO_SUBMIT_TRANSACTIONS_PENDING_TRANSACTIONS_WARNING_THRESHOLD", + "ESPRESSO_SUBMIT_TRANSACTIONS_PORT", + "ESPRESSO_SUBMIT_TRANSACTIONS_SLOW_TRANSACTION_WARNING_THRESHOLD", + "ESPRESSO_SUBMIT_TRANSACTIONS_SUBMIT_URL", + "FROM", + "TO" +] diff --git a/sequencer/build.rs b/sequencer/build.rs new file mode 100644 index 000000000..08db03e5c --- /dev/null +++ b/sequencer/build.rs @@ -0,0 +1,11 @@ +use vergen::EmitBuilder; + +pub fn main() -> anyhow::Result<()> { + // Set an environment variable with git information + EmitBuilder::builder() + .git_sha(false) + .git_describe(true, true, None) + .git_commit_timestamp() + .emit()?; + Ok(()) +} diff --git a/sequencer/src/api.rs b/sequencer/src/api.rs index 819534df2..6e5b64d79 100644 --- a/sequencer/src/api.rs +++ b/sequencer/src/api.rs @@ -1,13 +1,19 @@ -use self::data_source::StateSignatureDataSource; +use self::data_source::{HotShotConfigDataSource, PublicHotShotConfig, StateSignatureDataSource}; use crate::{ - network, persistence::SequencerPersistence, state::ValidatedState, - state_signature::StateSigner, Node, NodeState, SeqTypes, SequencerContext, Transaction, + network, + persistence::{ChainConfigPersistence, SequencerPersistence}, + state::{BlockMerkleTree, FeeAccountProof}, + state_signature::StateSigner, + ChainConfig, NamespaceId, Node, NodeState, PubKey, SeqTypes, SequencerContext, Transaction, }; +use anyhow::{bail, Context}; use async_once_cell::Lazy; use async_std::sync::{Arc, RwLock}; use async_trait::async_trait; -use data_source::{StateDataSource, SubmitDataSource}; +use committable::Commitment; +use data_source::{CatchupDataSource, SubmitDataSource}; use derivative::Derivative; +use ethers::prelude::{Address, U256}; use futures::{ future::{BoxFuture, Future, FutureExt}, stream::{BoxStream, Stream}, @@ -15,7 +21,10 @@ use futures::{ use hotshot::types::{Event, SystemContextHandle}; use hotshot_events_service::events_source::{BuilderEvent, EventsSource, EventsStreamer}; use hotshot_query_service::data_source::ExtensibleDataSource; -use hotshot_types::{data::ViewNumber, light_client::StateSignatureRequestBody}; +use hotshot_state_prover::service::light_client_genesis_from_stake_table; +use hotshot_types::{data::ViewNumber, light_client::StateSignatureRequestBody, HotShotConfig}; +use jf_merkle_tree::MerkleTreeScheme; +use serde::{Deserialize, Serialize}; use std::pin::Pin; use vbs::version::StaticVersionType; @@ -28,6 +37,20 @@ mod update; pub use options::Options; +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AccountQueryData { + pub balance: U256, + pub proof: FeeAccountProof, +} + +impl From<(FeeAccountProof, U256)> for AccountQueryData { + fn from((proof, balance): (FeeAccountProof, U256)) -> Self { + Self { balance, proof } + } +} + +pub type BlocksFrontier = ::MembershipProof; + type BoxLazy = Pin>>>; #[derive(Derivative)] @@ -38,7 +61,7 @@ struct ConsensusState>, + handle: Arc>>>, } impl @@ -47,9 +70,9 @@ impl) -> Self { Self { state_signer: ctx.state_signer(), - event_streamer: ctx.get_event_streamer(), + event_streamer: ctx.event_streamer(), node_state: ctx.node_state(), - handle: ctx.consensus().clone(), + handle: ctx.consensus(), } } } @@ -76,7 +99,7 @@ impl impl Stream> + Unpin { let state = self.clone(); - async move { state.consensus().await.get_event_stream() } + async move { state.consensus().await.read().await.event_stream() } .boxed() .flatten_stream() } @@ -89,13 +112,27 @@ impl &SystemContextHandle> { - &self.consensus.as_ref().get().await.get_ref().handle + async fn consensus(&self) -> Arc>>> { + Arc::clone(&self.consensus.as_ref().get().await.get_ref().handle) } async fn node_state(&self) -> &NodeState { &self.consensus.as_ref().get().await.get_ref().node_state } + + async fn hotshot_config(&self) -> HotShotConfig { + self.consensus + .as_ref() + .get() + .await + .get_ref() + .handle + .read() + .await + .hotshot + .config + .clone() + } } type StorageState = ExtensibleDataSource>; @@ -132,36 +169,163 @@ impl for ApiState { async fn submit(&self, tx: Transaction) -> anyhow::Result<()> { - self.consensus().await.submit_transaction(tx).await?; + self.consensus() + .await + .read() + .await + .submit_transaction(tx) + .await?; Ok(()) } } impl< N: network::Type, - D: Send + Sync, Ver: StaticVersionType + 'static, P: SequencerPersistence, - > StateDataSource for StorageState + D: CatchupDataSource + Send + Sync, + > CatchupDataSource for StorageState { - async fn get_decided_state(&self) -> Arc { - self.as_ref().get_decided_state().await + #[tracing::instrument(skip(self))] + async fn get_account( + &self, + height: u64, + view: ViewNumber, + account: Address, + ) -> anyhow::Result { + // Check if we have the desired state in memory. + match self.as_ref().get_account(height, view, account).await { + Ok(account) => return Ok(account), + Err(err) => { + tracing::info!("account is not in memory, trying storage: {err:#}"); + } + } + + // Try storage. + self.inner().get_account(height, view, account).await } - async fn get_undecided_state(&self, view: ViewNumber) -> Option> { - self.as_ref().get_undecided_state(view).await + #[tracing::instrument(skip(self))] + async fn get_frontier(&self, height: u64, view: ViewNumber) -> anyhow::Result { + // Check if we have the desired state in memory. + match self.as_ref().get_frontier(height, view).await { + Ok(frontier) => return Ok(frontier), + Err(err) => { + tracing::info!("frontier is not in memory, trying storage: {err:#}"); + } + } + + // Try storage. + self.inner().get_frontier(height, view).await + } + + async fn get_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + // Check if we have the desired state in memory. + match self.as_ref().get_chain_config(commitment).await { + Ok(cf) => return Ok(cf), + Err(err) => { + tracing::info!("chain config is not in memory, trying storage: {err:#}"); + } + } + + // Try storage. + self.inner().get_chain_config(commitment).await + } +} + +#[async_trait] +impl< + N: network::Type, + Ver: StaticVersionType + 'static, + P: SequencerPersistence, + D: ChainConfigPersistence + Send + Sync, + > ChainConfigPersistence for StorageState +{ + async fn insert_chain_config(&mut self, chain_config: ChainConfig) -> anyhow::Result<()> { + self.inner_mut().insert_chain_config(chain_config).await + } + async fn load_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + self.inner().load_chain_config(commitment).await } } -impl StateDataSource +impl CatchupDataSource for ApiState { - async fn get_decided_state(&self) -> Arc { - self.consensus().await.get_decided_state().await + #[tracing::instrument(skip(self))] + async fn get_account( + &self, + height: u64, + view: ViewNumber, + account: Address, + ) -> anyhow::Result { + let state = self + .consensus() + .await + .read() + .await + .state(view) + .await + .context(format!( + "state not available for height {height}, view {view:?}" + ))?; + let (proof, balance) = FeeAccountProof::prove(&state.fee_merkle_tree, account).context( + format!("account {account} not available for height {height}, view {view:?}"), + )?; + Ok(AccountQueryData { balance, proof }) + } + + #[tracing::instrument(skip(self))] + async fn get_frontier(&self, height: u64, view: ViewNumber) -> anyhow::Result { + let state = self + .consensus() + .await + .read() + .await + .state(view) + .await + .context(format!( + "state not available for height {height}, view {view:?}" + ))?; + let tree = &state.block_merkle_tree; + let frontier = tree.lookup(tree.num_leaves() - 1).expect_ok()?.1; + Ok(frontier) } - async fn get_undecided_state(&self, view: ViewNumber) -> Option> { - self.consensus().await.get_state(view).await + async fn get_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + let state = self.consensus().await.read().await.decided_state().await; + let chain_config = state.chain_config; + + if chain_config.commit() == commitment { + chain_config.resolve().context("chain config found") + } else { + bail!("chain config not found") + } + } +} + +impl + HotShotConfigDataSource for StorageState +{ + async fn get_config(&self) -> PublicHotShotConfig { + self.as_ref().hotshot_config().await.into() + } +} + +impl + HotShotConfigDataSource for ApiState +{ + async fn get_config(&self) -> PublicHotShotConfig { + self.hotshot_config().await.into() } } @@ -183,39 +347,42 @@ impl { pub server: SequencerContext, @@ -223,23 +390,54 @@ mod test_helpers { pub cfg: TestConfig, } + #[derive(Clone, Debug)] + pub struct TestNetworkUpgrades { + pub upgrades: BTreeMap, + pub start_proposing_view: u64, + pub stop_proposing_view: u64, + pub start_voting_view: u64, + pub stop_voting_view: u64, + } + impl TestNetwork

{ pub async fn with_state( - opt: Options, + api_config: Options, state: [ValidatedState; TestConfig::NUM_NODES], - persistence: [P; TestConfig::NUM_NODES], + persistence: [impl PersistenceOptions; TestConfig::NUM_NODES], catchup: [impl StateCatchup + 'static; TestConfig::NUM_NODES], + l1: Url, + upgrades: Option, + builder_port: Option, ) -> Self { - let mut cfg = TestConfig::default(); + let mut cfg = TestConfig::default_with_l1(l1); + cfg.builder_port = builder_port; + if let Some(upgrades) = upgrades { + cfg.set_upgrade_parameters( + upgrades.start_proposing_view, + upgrades.stop_proposing_view, + upgrades.start_voting_view, + upgrades.stop_voting_view, + ); + } - let (builder_task, builder_url) = run_test_builder().await; + Self::with_state_and_config(api_config, state, persistence, catchup, cfg).await + } - cfg.set_builder_url(builder_url); + pub async fn with_state_and_config( + api_config: Options, + state: [ValidatedState; TestConfig::NUM_NODES], + persistence: [impl PersistenceOptions; TestConfig::NUM_NODES], + catchup: [impl StateCatchup + 'static; TestConfig::NUM_NODES], + mut network_config: TestConfig, + ) -> Self { + let (builder_task, builder_url) = run_test_builder(network_config.builder_port).await; + network_config.set_builder_urls(vec1::vec1![builder_url]); let mut nodes = join_all(izip!(state, persistence, catchup).enumerate().map( |(i, (state, persistence, catchup))| { - let opt = opt.clone(); - let cfg = &cfg; + let opt = api_config.clone(); + let cfg = &network_config; + let upgrades_map = cfg.upgrades.clone().map(|e| e.upgrades).unwrap_or_default(); async move { if i == 0 { opt.serve( @@ -254,7 +452,7 @@ mod test_helpers { &*metrics, STAKE_TABLE_CAPACITY_FOR_TEST, SEQUENCER_VERSION, - true, + upgrades_map, ) .await } @@ -273,7 +471,7 @@ mod test_helpers { &NoMetrics, STAKE_TABLE_CAPACITY_FOR_TEST, SEQUENCER_VERSION, - true, + upgrades_map, ) .await } @@ -285,9 +483,7 @@ mod test_helpers { let handle_0 = &nodes[0]; // Hook the builder up to the event stream from the first node - if let Some(builder_task) = builder_task { - builder_task.start(Box::new(handle_0.get_event_stream())); - } + builder_task.start(Box::new(handle_0.event_stream().await)); for ctx in &nodes { ctx.start_consensus().await; @@ -296,23 +492,58 @@ mod test_helpers { let server = nodes.remove(0); let peers = nodes; - Self { server, peers, cfg } + Self { + server, + peers, + cfg: network_config, + } + } + + pub async fn new_with_config( + api_config: Options, + persistence: [impl PersistenceOptions; TestConfig::NUM_NODES], + network_config: TestConfig, + ) -> Self { + Self::with_state_and_config( + api_config, + Default::default(), + persistence, + std::array::from_fn(|_| MockStateCatchup::default()), + network_config, + ) + .await } - pub async fn new(opt: Options, persistence: [P; TestConfig::NUM_NODES]) -> Self { + // TODO: Remove this constructor and rename `new_with_config` to `new`. + // https://github.com/EspressoSystems/espresso-sequencer/issues/1603 + pub async fn new( + api_config: Options, + persistence: [impl PersistenceOptions; TestConfig::NUM_NODES], + l1: Url, + builder_port: Option, + ) -> Self { Self::with_state( - opt, + api_config, Default::default(), persistence, std::array::from_fn(|_| MockStateCatchup::default()), + l1, + Default::default(), + builder_port, ) .await } + pub fn light_client_genesis(&self) -> ParsedLightClientState { + let st = self.cfg.stake_table(); + light_client_genesis_from_stake_table(st).unwrap() + } + pub async fn stop_consensus(&mut self) { - self.server.consensus_mut().shut_down().await; + self.server.shutdown_consensus().await; + for ctx in &mut self.peers { - ctx.consensus_mut().shut_down().await; + ctx.shutdown_consensus().await; } } } @@ -332,8 +563,16 @@ mod test_helpers { let url = format!("http://localhost:{port}").parse().unwrap(); let client: Client = Client::new(url); - let options = opt(Options::from(options::Http { port }).status(Default::default())); - let _network = TestNetwork::new(options, [NoStorage; TestConfig::NUM_NODES]).await; + let options = opt(Options::with_port(port).status(Default::default())); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + let _network = TestNetwork::new( + options, + [no_storage::Options; TestConfig::NUM_NODES], + l1, + None, + ) + .await; client.connect(None).await; // The status API is well tested in the query service repo. Here we are just smoke testing @@ -371,16 +610,24 @@ mod test_helpers { setup_logging(); setup_backtrace(); - let txn = Transaction::new(Default::default(), vec![1, 2, 3, 4]); + let txn = Transaction::new(NamespaceId::from(1), vec![1, 2, 3, 4]); let port = pick_unused_port().expect("No ports free"); let url = format!("http://localhost:{port}").parse().unwrap(); let client: Client = Client::new(url); - let options = opt(Options::from(options::Http { port }).submit(Default::default())); - let network = TestNetwork::new(options, [NoStorage; TestConfig::NUM_NODES]).await; - let mut events = network.server.get_event_stream(); + let options = opt(Options::with_port(port).submit(Default::default())); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + let network = TestNetwork::new( + options, + [no_storage::Options; TestConfig::NUM_NODES], + l1, + None, + ) + .await; + let mut events = network.server.event_stream().await; client.connect(None).await; @@ -407,19 +654,22 @@ mod test_helpers { let url = format!("http://localhost:{port}").parse().unwrap(); let client: Client = Client::new(url); - let options = opt(Options::from(options::Http { port })); - let network = TestNetwork::new(options, [NoStorage; TestConfig::NUM_NODES]).await; + let options = opt(Options::with_port(port)); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + let network = TestNetwork::new( + options, + [no_storage::Options; TestConfig::NUM_NODES], + l1, + None, + ) + .await; let mut height: u64; // Wait for block >=2 appears // It's waiting for an extra second to make sure that the signature is generated loop { - height = network - .server - .consensus() - .get_decided_leaf() - .await - .get_height(); + height = network.server.decided_leaf().await.height(); sleep(std::time::Duration::from_secs(1)).await; if height >= 2 { break; @@ -433,14 +683,14 @@ mod test_helpers { .unwrap(); } - /// Test the state API with custom options. + /// Test the catchup API with custom options. /// /// The `opt` function can be used to modify the [`Options`] which are used to start the server. /// By default, the options are the minimal required to run this test (configuring a port and - /// enabling the state API). `opt` may add additional functionality (e.g. adding a query module + /// enabling the catchup API). `opt` may add additional functionality (e.g. adding a query module /// to test a different initialization path) but should not remove or modify the existing - /// functionality (e.g. removing the state module or changing the port). - pub async fn state_test_helper(opt: impl FnOnce(Options) -> Options) { + /// functionality (e.g. removing the catchup module or changing the port). + pub async fn catchup_test_helper(opt: impl FnOnce(Options) -> Options) { setup_logging(); setup_backtrace(); @@ -448,12 +698,20 @@ mod test_helpers { let url = format!("http://localhost:{port}").parse().unwrap(); let client: Client = Client::new(url); - let options = opt(Options::from(options::Http { port }).catchup(Default::default())); - let mut network = TestNetwork::new(options, [NoStorage; TestConfig::NUM_NODES]).await; + let options = opt(Options::with_port(port).catchup(Default::default())); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + let network = TestNetwork::new( + options, + [no_storage::Options; TestConfig::NUM_NODES], + l1, + None, + ) + .await; client.connect(None).await; // Wait for a few blocks to be decided. - let mut events = network.server.get_event_stream(); + let mut events = network.server.event_stream().await; loop { if let Event { event: EventType::Decide { leaf_chain, .. }, @@ -462,7 +720,7 @@ mod test_helpers { { if leaf_chain .iter() - .any(|LeafInfo { leaf, .. }| leaf.get_block_header().height > 2) + .any(|LeafInfo { leaf, .. }| leaf.block_header().height > 2) { break; } @@ -470,37 +728,19 @@ mod test_helpers { } // Stop consensus running on the node so we freeze the decided and undecided states. - network.server.consensus_mut().shut_down().await; - - // Decided fee state: absent account. - let res = client - .get::(&format!("catchup/account/{:x}", Address::default())) - .send() - .await - .unwrap(); - assert_eq!(res.balance, 0.into()); - assert_eq!( - res.proof - .verify( - &network - .server - .consensus() - .get_decided_state() - .await - .fee_merkle_tree - .commitment() - ) - .unwrap(), - 0.into() - ); + // We'll let it go out of scope here since it's a write lock. + { + network.server.shutdown_consensus().await; + } // Undecided fee state: absent account. - let leaf = network.server.consensus().get_decided_leaf().await; - let view = leaf.get_view_number() + 1; + let leaf = network.server.decided_leaf().await; + let height = leaf.height() + 1; + let view = leaf.view_number() + 1; let res = client .get::(&format!( - "catchup/{}/account/{:x}", - view.get_u64(), + "catchup/{height}/{}/account/{:x}", + view.u64(), Address::default() )) .send() @@ -512,8 +752,7 @@ mod test_helpers { .verify( &network .server - .consensus() - .get_state(view) + .state(view) .await .unwrap() .fee_merkle_tree @@ -523,33 +762,15 @@ mod test_helpers { 0.into() ); - // Decided block state. - let res = client - .get::("catchup/blocks") - .send() - .await - .unwrap(); - let root = &network - .server - .consensus() - .get_decided_state() - .await - .block_merkle_tree - .commitment(); - BlockMerkleTree::verify(root.digest(), root.size() - 1, res) - .unwrap() - .unwrap(); - // Undecided block state. let res = client - .get::(&format!("catchup/{}/blocks", view.get_u64())) + .get::(&format!("catchup/{height}/{}/blocks", view.u64())) .send() .await .unwrap(); let root = &network .server - .consensus() - .get_state(view) + .state(view) .await .unwrap() .block_merkle_tree @@ -567,22 +788,22 @@ mod api_tests { use super::*; use crate::{ - persistence::no_storage::NoStorage, + persistence::no_storage, testing::{wait_for_decide_on_handle, TestConfig}, - Header, + Header, NamespaceId, }; use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; use committable::Committable; use data_source::testing::TestableSequencerDataSource; use endpoints::NamespaceProofQueryData; use es_version::SequencerVersion; + use ethers::utils::Anvil; use futures::stream::StreamExt; - use hotshot_query_service::availability::LeafQueryData; - use hotshot_types::vid::vid_scheme; + use hotshot_query_service::availability::{LeafQueryData, VidCommonQueryData}; use portpicker::pick_unused_port; use surf_disco::Client; use test_helpers::{ - state_signature_test_helper, state_test_helper, status_test_helper, submit_test_helper, + catchup_test_helper, state_signature_test_helper, status_test_helper, submit_test_helper, TestNetwork, }; use tide_disco::error::ServerError; @@ -610,18 +831,23 @@ mod api_tests { setup_logging(); setup_backtrace(); - let vid = vid_scheme(5); - let txn = Transaction::new(Default::default(), vec![1, 2, 3, 4]); + // Arbitrary transaction, arbitrary namespace ID + let ns_id = NamespaceId::from(42); + let txn = Transaction::new(ns_id, vec![1, 2, 3, 4]); // Start query service. let port = pick_unused_port().expect("No ports free"); let storage = D::create_storage().await; + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); let network = TestNetwork::new( - D::options(&storage, options::Http { port }.into()).submit(Default::default()), - [NoStorage; TestConfig::NUM_NODES], + D::options(&storage, Options::with_port(port)).submit(Default::default()), + [no_storage::Options; TestConfig::NUM_NODES], + l1, + None, ) .await; - let mut events = network.server.get_event_stream(); + let mut events = network.server.event_stream().await; // Connect client. let client: Client = @@ -660,14 +886,31 @@ mod api_tests { .await .unwrap(); let ns_query_res: NamespaceProofQueryData = client - .get(&format!("availability/block/{block_num}/namespace/0")) + .get(&format!("availability/block/{block_num}/namespace/{ns_id}")) .send() .await .unwrap(); - ns_query_res - .proof - .verify(&vid, &header.payload_commitment, &header.ns_table) - .unwrap(); + + // Verify namespace proof if present + if let Some(ns_proof) = ns_query_res.proof { + let vid_common: VidCommonQueryData = client + .get(&format!("availability/vid/common/{block_num}")) + .send() + .await + .unwrap(); + + ns_proof + .verify( + &header.ns_table, + &header.payload_commitment, + vid_common.common(), + ) + .unwrap(); + } else { + // Namespace proof should be present if ns_id exists in ns_table + assert!(header.ns_table.find_ns_id(&ns_id).is_none()); + assert!(ns_query_res.transactions.is_empty()); + } found_empty_block = found_empty_block || ns_query_res.transactions.is_empty(); @@ -683,9 +926,9 @@ mod api_tests { } #[async_std::test] - pub(crate) async fn state_test_with_query_module() { + pub(crate) async fn catchup_test_with_query_module() { let storage = D::create_storage().await; - state_test_helper(|opt| D::options(&storage, opt)).await + catchup_test_helper(|opt| D::options(&storage, opt)).await } #[async_std::test] @@ -711,12 +954,17 @@ mod api_tests { let client: Client = Client::new(url); - let options = Options::from(options::Http { - port: query_service_port, - }) - .hotshot_events(hotshot_events); + let options = Options::with_port(query_service_port).hotshot_events(hotshot_events); - let _network = TestNetwork::new(options, [NoStorage; TestConfig::NUM_NODES]).await; + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + let _network = TestNetwork::new( + options, + [no_storage::Options; TestConfig::NUM_NODES], + l1, + None, + ) + .await; let mut subscribed_events = client .socket("hotshot-events/events") @@ -752,19 +1000,19 @@ mod test { use super::*; use crate::{ catchup::{mock::MockStateCatchup, StatePeers}, - persistence::no_storage::NoStorage, - state::{FeeAccount, FeeAmount}, + genesis::{Upgrade, UpgradeType}, + persistence::no_storage, + state::{FeeAccount, FeeAmount, ValidatedState}, testing::TestConfig, Header, }; use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; use async_std::task::sleep; - use committable::Commitment; + use committable::{Commitment, Committable}; use es_version::{SequencerVersion, SEQUENCER_VERSION}; - use futures::{ - future::{self, join_all}, - stream::{StreamExt, TryStreamExt}, - }; + use ethers::utils::Anvil; + use futures::future::{self, join_all}; + use futures::stream::{StreamExt, TryStreamExt}; use hotshot::types::EventType; use hotshot_query_service::{ availability::{BlockQueryData, LeafQueryData}, @@ -774,15 +1022,16 @@ mod test { event::LeafInfo, traits::{metrics::NoMetrics, node_implementation::ConsensusTime}, }; - use jf_primitives::merkle_tree::prelude::{MerkleProof, Sha3Node}; + use jf_merkle_tree::prelude::{MerkleProof, Sha3Node}; use portpicker::pick_unused_port; use std::time::Duration; use surf_disco::Client; use test_helpers::{ - state_signature_test_helper, state_test_helper, status_test_helper, submit_test_helper, - TestNetwork, + catchup_test_helper, state_signature_test_helper, status_test_helper, submit_test_helper, + TestNetwork, TestNetworkUpgrades, }; use tide_disco::{app::AppHealth, error::ServerError, healthcheck::HealthStatus}; + use vbs::version::Version; #[async_std::test] async fn test_healthcheck() { @@ -792,8 +1041,16 @@ mod test { let port = pick_unused_port().expect("No ports free"); let url = format!("http://localhost:{port}").parse().unwrap(); let client: Client = Client::new(url); - let options = Options::from(options::Http { port }); - let _network = TestNetwork::new(options, [NoStorage; TestConfig::NUM_NODES]).await; + let options = Options::with_port(port); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + let _network = TestNetwork::new( + options, + [no_storage::Options; TestConfig::NUM_NODES], + l1, + None, + ) + .await; client.connect(None).await; let health = client.get::("healthcheck").send().await.unwrap(); @@ -816,8 +1073,8 @@ mod test { } #[async_std::test] - async fn state_test_without_query_module() { - state_test_helper(|opt| opt).await + async fn catchup_test_without_query_module() { + catchup_test_helper(|opt| opt).await } #[async_std::test] @@ -830,12 +1087,20 @@ mod test { let storage = SqlDataSource::create_storage().await; let options = SqlDataSource::options( &storage, - Options::from(options::Http { port }) + Options::with_port(port) .state(Default::default()) .status(Default::default()), ); - let mut network = TestNetwork::new(options, [NoStorage; TestConfig::NUM_NODES]).await; + let anvil: ethers::utils::AnvilInstance = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + let mut network = TestNetwork::new( + options, + [no_storage::Options; TestConfig::NUM_NODES], + l1, + None, + ) + .await; let url = format!("http://localhost:{port}").parse().unwrap(); let client: Client = Client::new(url); @@ -894,26 +1159,32 @@ mod test { // Start a sequencer network, using the query service for catchup. let port = pick_unused_port().expect("No ports free"); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); let mut network = TestNetwork::with_state( - Options::from(options::Http { port }).catchup(Default::default()), + Options::with_port(port).catchup(Default::default()), Default::default(), - [NoStorage; TestConfig::NUM_NODES], + [no_storage::Options; TestConfig::NUM_NODES], std::array::from_fn(|_| { - StatePeers::::from_urls(vec![format!("http://localhost:{port}") - .parse() - .unwrap()]) + StatePeers::::from_urls( + vec![format!("http://localhost:{port}",).parse().unwrap()], + Default::default(), + ) }), + l1, + None, + None, ) .await; // Wait for replica 0 to reach a (non-genesis) decide, before disconnecting it. - let mut events = network.peers[0].get_event_stream(); + let mut events = network.peers[0].event_stream().await; loop { let event = events.next().await.unwrap(); let EventType::Decide { leaf_chain, .. } = event.event else { continue; }; - if leaf_chain[0].leaf.get_height() > 0 { + if leaf_chain[0].leaf.height() > 0 { break; } } @@ -928,7 +1199,8 @@ mod test { // Wait for a few blocks to pass while the node is down, so it falls behind. network .server - .get_event_stream() + .event_stream() + .await .filter(|event| future::ready(matches!(event.event, EventType::Decide { .. }))) .take(3) .collect::>() @@ -940,17 +1212,18 @@ mod test { .init_node( 1, ValidatedState::default(), - NoStorage, - StatePeers::::from_urls(vec![format!("http://localhost:{port}") - .parse() - .unwrap()]), + no_storage::Options, + StatePeers::::from_urls( + vec![format!("http://localhost:{port}").parse().unwrap()], + Default::default(), + ), &NoMetrics, test_helpers::STAKE_TABLE_CAPACITY_FOR_TEST, SEQUENCER_VERSION, - true, + Default::default(), ) .await; - let mut events = node.get_event_stream(); + let mut events = node.event_stream().await; // Wait for a (non-genesis) block proposed by each node, to prove that the lagging node has // caught up and all nodes are in sync. @@ -961,9 +1234,8 @@ mod test { continue; }; for LeafInfo { leaf, .. } in leaf_chain.iter().rev() { - let height = leaf.get_height(); - let leaf_builder = - (leaf.get_view_number().get_u64() as usize) % TestConfig::NUM_NODES; + let height = leaf.height(); + let leaf_builder = (leaf.view_number().u64() as usize) % TestConfig::NUM_NODES; if height == 0 { continue; } @@ -980,6 +1252,245 @@ mod test { } } + #[async_std::test] + async fn test_chain_config_from_instance() { + // This test uses a ValidatedState which only has the default chain config commitment. + // The NodeState has the full chain config. + // Both chain config commitments will match, so the ValidatedState should have the full chain config after a non-genesis block is decided. + setup_logging(); + setup_backtrace(); + + let port = pick_unused_port().expect("No ports free"); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + + let chain_config: ChainConfig = ChainConfig::default(); + + let state = ValidatedState { + chain_config: chain_config.commit().into(), + ..Default::default() + }; + + let states = std::array::from_fn(|_| state.clone()); + + let mut network = TestNetwork::with_state( + Options::with_port(port).catchup(Default::default()), + states, + [no_storage::Options; TestConfig::NUM_NODES], + std::array::from_fn(|_| { + StatePeers::::from_urls( + vec![format!("http://localhost:{port}").parse().unwrap()], + Default::default(), + ) + }), + l1, + None, + None, + ) + .await; + + // Wait for few blocks to be decided. + network + .server + .event_stream() + .await + .filter(|event| future::ready(matches!(event.event, EventType::Decide { .. }))) + .take(3) + .collect::>() + .await; + + for peer in &network.peers { + let state = peer.consensus().read().await.decided_state().await; + + assert_eq!(state.chain_config.resolve().unwrap(), chain_config) + } + + network.server.shut_down().await; + drop(network); + } + + #[async_std::test] + async fn test_chain_config_catchup() { + // This test uses a ValidatedState with a non-default chain config + // so it will be different from the NodeState chain config used by the TestNetwork. + // However, for this test to work, at least one node should have a full chain config + // to allow other nodes to catch up. + + setup_logging(); + setup_backtrace(); + + let port = pick_unused_port().expect("No ports free"); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + + let cf = ChainConfig { + max_block_size: 300.into(), + base_fee: 1.into(), + ..Default::default() + }; + + // State1 contains only the chain config commitment + let state1 = ValidatedState { + chain_config: cf.commit().into(), + ..Default::default() + }; + + //state 2 contains the full chain config + let state2 = ValidatedState { + chain_config: cf.into(), + ..Default::default() + }; + + let mut states = std::array::from_fn(|_| state1.clone()); + // only one node has the full chain config + // all the other nodes should do a catchup to get the full chain config from peer 0 + states[0] = state2; + + let mut network = TestNetwork::with_state( + Options::from(options::Http { + port, + max_connections: None, + }) + .catchup(Default::default()), + states, + [no_storage::Options; TestConfig::NUM_NODES], + std::array::from_fn(|_| { + StatePeers::::from_urls( + vec![format!("http://localhost:{port}").parse().unwrap()], + Default::default(), + ) + }), + l1, + None, + None, + ) + .await; + + // Wait for a few blocks to be decided. + network + .server + .event_stream() + .await + .filter(|event| future::ready(matches!(event.event, EventType::Decide { .. }))) + .take(3) + .collect::>() + .await; + + for peer in &network.peers { + let state = peer.consensus().read().await.decided_state().await; + + assert_eq!(state.chain_config.resolve().unwrap(), cf) + } + + network.server.shut_down().await; + drop(network); + } + + #[async_std::test] + async fn test_chain_config_upgrade() { + setup_logging(); + setup_backtrace(); + + let port = pick_unused_port().expect("No ports free"); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + + let chain_config_upgrade = ChainConfig { + max_block_size: 300.into(), + base_fee: 1.into(), + ..Default::default() + }; + let mut map = std::collections::BTreeMap::new(); + let view = 5; + let propose_window = 10; + map.insert( + Version { major: 0, minor: 2 }, + Upgrade { + view, + propose_window, + upgrade_type: UpgradeType::ChainConfig { + chain_config: chain_config_upgrade, + }, + }, + ); + + let stop_voting_view = 100; + let upgrades = TestNetworkUpgrades { + upgrades: map, + start_proposing_view: view, + stop_proposing_view: view + propose_window, + start_voting_view: 1, + stop_voting_view, + }; + + let mut network = TestNetwork::with_state( + Options::from(options::Http { + port, + max_connections: None, + }) + .catchup(Default::default()) + .status(Default::default()), + Default::default(), + [no_storage::Options; TestConfig::NUM_NODES], + std::array::from_fn(|_| { + StatePeers::::from_urls( + vec![format!("http://localhost:{port}").parse().unwrap()], + Default::default(), + ) + }), + l1, + Some(upgrades), + None, + ) + .await; + + let mut events = network.server.event_stream().await; + loop { + let event = events.next().await.unwrap(); + + match event.event { + EventType::UpgradeProposal { proposal, .. } => { + let upgrade = proposal.data.upgrade_proposal; + let new_version = upgrade.new_version; + assert_eq!(new_version, Version { major: 0, minor: 2 }); + break; + } + _ => continue, + } + } + + let client: Client = + Client::new(format!("http://localhost:{port}").parse().unwrap()); + client.connect(None).await; + tracing::info!(port, "server running"); + + 'outer: loop { + let height = client + .get::("status/block-height") + .send() + .await + .unwrap(); + + for peer in &network.peers { + let state = peer.consensus().read().await.decided_state().await; + + match state.chain_config.resolve() { + Some(cf) => { + if cf != chain_config_upgrade && height as u64 > stop_voting_view { + panic!("failed to upgrade chain config"); + } + } + None => continue 'outer, + } + } + + break; + } + + network.server.shut_down().await; + drop(network); + } + #[async_std::test] pub(crate) async fn test_restart() { setup_logging(); @@ -988,22 +1499,25 @@ mod test { // Initialize nodes. let storage = join_all((0..TestConfig::NUM_NODES).map(|_| SqlDataSource::create_storage())).await; - let persistence = join_all( - storage - .iter() - .map(::connect), - ) - .await - .try_into() - .unwrap(); + let persistence = storage + .iter() + .map(::persistence_options) + .collect::>() + .try_into() + .unwrap(); let port = pick_unused_port().unwrap(); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); let mut network = TestNetwork::with_state( - SqlDataSource::options(&storage[0], options::Http { port }.into()) + SqlDataSource::options(&storage[0], Options::with_port(port)) .state(Default::default()) .status(Default::default()), Default::default(), persistence, std::array::from_fn(|_| MockStateCatchup::default()), + l1, + None, + None, ) .await; @@ -1045,40 +1559,28 @@ mod test { .try_collect() .await .unwrap(); - let decided_view = chain.last().unwrap().leaf().get_view_number(); + let decided_view = chain.last().unwrap().leaf().view_number(); // Get the most recent state, for catchup. - let state = network.server.consensus().get_decided_state().await; - tracing::info!(?decided_view, ?state, "consensus state"); - // Wait for merklized state storage to update. - while let Err(err) = client - .get::<()>(&format!("block-state/{}/{}", height - 1, height - 2)) - .send() - .await - { - tracing::info!( - height, - "waiting for merklized state to become available ({err:#})" - ); - sleep(Duration::from_secs(1)).await; - } + let state = network.server.decided_state().await; + tracing::info!(?decided_view, ?state, "consensus state"); // Fully shut down the API servers. drop(network); // Start up again, resuming from the last decided leaf. let port = pick_unused_port().expect("No ports free"); - let persistence = join_all( - storage - .iter() - .map(::connect), - ) - .await - .try_into() - .unwrap(); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + let persistence = storage + .iter() + .map(::persistence_options) + .collect::>() + .try_into() + .unwrap(); let _network = TestNetwork::with_state( - SqlDataSource::options(&storage[0], options::Http { port }.into()) + SqlDataSource::options(&storage[0], Options::with_port(port)) .catchup(Default::default()), Default::default(), persistence, @@ -1086,10 +1588,14 @@ mod test { // Catchup using node 0 as a peer. Node 0 was running the archival state service // before the restart, so it should be able to resume without catching up by loading // state from storage. - StatePeers::::from_urls(vec![format!("http://localhost:{port}") - .parse() - .unwrap()]) + StatePeers::::from_urls( + vec![format!("http://localhost:{port}").parse().unwrap()], + Default::default(), + ) }), + l1, + None, + None, ) .await; let client: Client = @@ -1110,7 +1616,7 @@ mod test { .unwrap(); assert_eq!(new_leaf.height(), height as u64); assert_eq!( - new_leaf.leaf().get_parent_commitment(), + new_leaf.leaf().parent_commitment(), chain[height - 1].hash() ); diff --git a/sequencer/src/api/data_source.rs b/sequencer/src/api/data_source.rs index 6cdaf3e9f..392f55417 100644 --- a/sequencer/src/api/data_source.rs +++ b/sequencer/src/api/data_source.rs @@ -1,26 +1,36 @@ +use std::{num::NonZeroUsize, time::Duration}; + use super::{ fs, options::{Options, Query}, - sql, + sql, AccountQueryData, BlocksFrontier, }; use crate::{ network, persistence::{self, SequencerPersistence}, - state::ValidatedState, - SeqTypes, Transaction, + ChainConfig, PubKey, SeqTypes, Transaction, }; -use async_std::sync::Arc; +use anyhow::bail; use async_trait::async_trait; +use committable::Commitment; +use ethers::prelude::Address; +use futures::future::Future; use hotshot_query_service::{ availability::AvailabilityDataSource, - data_source::{UpdateDataSource, VersionedDataSource}, + data_source::{MetricsDataSource, UpdateDataSource, VersionedDataSource}, fetching::provider::{AnyProvider, QueryServiceProvider}, node::NodeDataSource, status::StatusDataSource, }; -use hotshot_types::{data::ViewNumber, light_client::StateSignatureRequestBody}; +use hotshot_types::{ + data::ViewNumber, light_client::StateSignatureRequestBody, ExecutionType, HotShotConfig, + PeerConfig, ValidatorConfig, +}; + +use serde::Serialize; use tide_disco::Url; use vbs::version::StaticVersionType; +use vec1::Vec1; pub trait DataSourceOptions: persistence::PersistenceOptions { type DataSource: SequencerDataSource; @@ -79,9 +89,12 @@ pub fn provider( provider } -#[trait_variant::make(SubmitDataSource: Send)] -pub(crate) trait LocalSubmitDataSource { - async fn submit(&self, tx: Transaction) -> anyhow::Result<()>; +pub(crate) trait SubmitDataSource { + fn submit(&self, tx: Transaction) -> impl Send + Future>; +} + +pub(crate) trait HotShotConfigDataSource { + fn get_config(&self) -> impl Send + Future; } #[async_trait] @@ -89,26 +102,199 @@ pub(crate) trait StateSignatureDataSource { async fn get_state_signature(&self, height: u64) -> Option; } -#[trait_variant::make(StateDataSource: Send)] -pub(crate) trait LocalStateDataSource { - async fn get_decided_state(&self) -> Arc; - async fn get_undecided_state(&self, view: ViewNumber) -> Option>; +pub(crate) trait CatchupDataSource { + /// Get the state of the requested `account`. + /// + /// The state is fetched from a snapshot at the given height and view, which _must_ correspond! + /// `height` is provided to simplify lookups for backends where data is not indexed by view. + /// This function is intended to be used for catchup, so `view` should be no older than the last + /// decided view. + fn get_account( + &self, + _height: u64, + _view: ViewNumber, + _account: Address, + ) -> impl Send + Future> { + // Merklized state catchup is only supported by persistence backends that provide merklized + // state storage. This default implementation is overridden for those that do. Otherwise, + // catchup can still be provided by fetching undecided merklized state from consensus + // memory. + async { + bail!("merklized state catchup is not supported for this data source"); + } + } + + /// Get the blocks Merkle tree frontier. + /// + /// The state is fetched from a snapshot at the given height and view, which _must_ correspond! + /// `height` is provided to simplify lookups for backends where data is not indexed by view. + /// This function is intended to be used for catchup, so `view` should be no older than the last + /// decided view. + fn get_frontier( + &self, + _height: u64, + _view: ViewNumber, + ) -> impl Send + Future> { + // Merklized state catchup is only supported by persistence backends that provide merklized + // state storage. This default implementation is overridden for those that do. Otherwise, + // catchup can still be provided by fetching undecided merklized state from consensus + // memory. + async { + bail!("merklized state catchup is not supported for this data source"); + } + } + + fn get_chain_config( + &self, + _commitment: Commitment, + ) -> impl Send + Future> { + async { + bail!("chain config catchup is not supported for this data source"); + } + } +} + +impl CatchupDataSource for MetricsDataSource {} + +/// This struct defines the public Hotshot validator configuration. +/// Private key and state key pairs are excluded for security reasons. + +#[derive(Debug, Serialize)] +pub struct PublicValidatorConfig { + pub public_key: PubKey, + pub stake_value: u64, + pub is_da: bool, + pub private_key: &'static str, + pub state_public_key: String, + pub state_key_pair: &'static str, +} + +impl From> for PublicValidatorConfig { + fn from(v: ValidatorConfig) -> Self { + let ValidatorConfig:: { + public_key, + private_key: _, + stake_value, + state_key_pair, + is_da, + } = v; + + let state_public_key = state_key_pair.ver_key(); + + Self { + public_key, + stake_value, + is_da, + state_public_key: state_public_key.to_string(), + private_key: "*****", + state_key_pair: "*****", + } + } +} + +/// This struct defines the public Hotshot configuration parameters. +/// Our config module features a GET endpoint accessible via the route `/hotshot` to display the hotshot config parameters. +/// Hotshot config has sensitive information like private keys and such fields are excluded from this struct. +#[derive(Debug, Serialize)] +pub struct PublicHotShotConfig { + pub execution_type: ExecutionType, + pub start_threshold: (u64, u64), + pub num_nodes_with_stake: NonZeroUsize, + pub num_nodes_without_stake: usize, + pub known_nodes_with_stake: Vec>, + pub known_da_nodes: Vec>, + pub known_nodes_without_stake: Vec, + pub my_own_validator_config: PublicValidatorConfig, + pub da_staked_committee_size: usize, + pub da_non_staked_committee_size: usize, + pub fixed_leader_for_gpuvid: usize, + pub next_view_timeout: u64, + pub view_sync_timeout: Duration, + pub timeout_ratio: (u64, u64), + pub round_start_delay: u64, + pub start_delay: u64, + pub num_bootstrap: usize, + pub builder_timeout: Duration, + pub data_request_delay: Duration, + pub builder_urls: Vec1, + pub start_proposing_view: u64, + pub stop_proposing_view: u64, + pub start_voting_view: u64, + pub stop_voting_view: u64, +} + +impl From> for PublicHotShotConfig { + fn from(v: HotShotConfig) -> Self { + // Destructure all fields from HotShotConfig to return an error + // if new fields are added to HotShotConfig. This makes sure that we handle + // all fields appropriately and do not miss any updates. + let HotShotConfig:: { + execution_type, + start_threshold, + num_nodes_with_stake, + num_nodes_without_stake, + known_nodes_with_stake, + known_da_nodes, + known_nodes_without_stake, + my_own_validator_config, + da_staked_committee_size, + da_non_staked_committee_size, + fixed_leader_for_gpuvid, + next_view_timeout, + view_sync_timeout, + timeout_ratio, + round_start_delay, + start_delay, + num_bootstrap, + builder_timeout, + data_request_delay, + builder_urls, + start_proposing_view, + stop_proposing_view, + start_voting_view, + stop_voting_view, + } = v; + + Self { + execution_type, + start_threshold, + num_nodes_with_stake, + num_nodes_without_stake, + known_nodes_with_stake, + known_da_nodes, + known_nodes_without_stake, + my_own_validator_config: my_own_validator_config.into(), + da_staked_committee_size, + da_non_staked_committee_size, + fixed_leader_for_gpuvid, + next_view_timeout, + view_sync_timeout, + timeout_ratio, + round_start_delay, + start_delay, + num_bootstrap, + builder_timeout, + data_request_delay, + builder_urls, + start_proposing_view, + stop_proposing_view, + start_voting_view, + stop_voting_view, + } + } } #[cfg(test)] pub(crate) mod testing { use super::super::Options; use super::*; - use crate::persistence::SequencerPersistence; - use std::fmt::Debug; #[async_trait] pub(crate) trait TestableSequencerDataSource: SequencerDataSource { - type Storage; - type Persistence: Debug + SequencerPersistence; + type Storage: Sync; async fn create_storage() -> Self::Storage; - async fn connect(storage: &Self::Storage) -> Self::Persistence; + fn persistence_options(storage: &Self::Storage) -> Self::Options; fn options(storage: &Self::Storage, opt: Options) -> Options; } } diff --git a/sequencer/src/api/endpoints.rs b/sequencer/src/api/endpoints.rs index 8029eb651..685e4ba61 100644 --- a/sequencer/src/api/endpoints.rs +++ b/sequencer/src/api/endpoints.rs @@ -1,30 +1,35 @@ //! Sequencer-specific API endpoint handlers. +use serde::de::Error as _; +use std::{ + collections::{BTreeSet, HashMap}, + env, +}; + use super::{ data_source::{ - SequencerDataSource, StateDataSource, StateSignatureDataSource, SubmitDataSource, + CatchupDataSource, HotShotConfigDataSource, SequencerDataSource, StateSignatureDataSource, + SubmitDataSource, }, StorageState, }; use crate::{ - block::payload::{parse_ns_payload, NamespaceProof}, - network, - persistence::SequencerPersistence, - state::{BlockMerkleTree, FeeAccountProof, ValidatedState}, - NamespaceId, SeqTypes, Transaction, + block::NsProof, network, persistence::SequencerPersistence, NamespaceId, SeqTypes, Transaction, }; use anyhow::Result; use async_std::sync::{Arc, RwLock}; use committable::Committable; -use ethers::prelude::U256; use futures::{try_join, FutureExt}; use hotshot_query_service::{ availability::{self, AvailabilityDataSource, CustomSnafu, FetchBlockSnafu}, - merklized_state::{self, MerklizedState, MerklizedStateDataSource}, + data_source::storage::ExplorerStorage, + explorer::{self}, + merklized_state::{ + self, MerklizedState, MerklizedStateDataSource, MerklizedStateHeightPersistence, + }, node, Error, }; use hotshot_types::{data::ViewNumber, traits::node_implementation::ConsensusTime}; -use jf_primitives::merkle_tree::MerkleTreeScheme; use serde::{Deserialize, Serialize}; use snafu::OptionExt; use tagged_base64::TaggedBase64; @@ -37,24 +42,10 @@ use vbs::version::StaticVersionType; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct NamespaceProofQueryData { - pub proof: NamespaceProof, + pub proof: Option, pub transactions: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct AccountQueryData { - pub balance: U256, - pub proof: FeeAccountProof, -} - -impl From<(FeeAccountProof, U256)> for AccountQueryData { - fn from((proof, balance): (FeeAccountProof, U256)) -> Self { - Self { balance, proof } - } -} - -pub type BlocksFrontier = ::MembershipProof; - pub(super) type AvailState = Arc>>; type AvailabilityApi = Api, availability::Error, Ver>; @@ -80,8 +71,7 @@ where api.get("getnamespaceproof", move |req, state| { async move { let height: usize = req.integer_param("height")?; - let ns_id: u64 = req.integer_param("namespace")?; - let ns_id = NamespaceId::from(ns_id); + let ns_id = NamespaceId::from(req.integer_param::<_, u32>("namespace")?); let (block, common) = try_join!( async move { state @@ -105,32 +95,25 @@ where } )?; - let proof = block - .payload() - .namespace_with_proof( - block.payload().get_ns_table(), - ns_id, - common.common().clone(), - ) - .context(CustomSnafu { - message: format!("failed to make proof for namespace {ns_id}"), - status: StatusCode::NotFound, - })?; - - let transactions = if let NamespaceProof::Existence { - ref ns_payload_flat, - .. - } = proof - { - parse_ns_payload(ns_payload_flat, ns_id) - } else { - Vec::new() - }; + if let Some(ns_index) = block.payload().ns_table().find_ns_id(&ns_id) { + let proof = NsProof::new(block.payload(), &ns_index, common.common()).context( + CustomSnafu { + message: format!("failed to make proof for namespace {ns_id}"), + status: StatusCode::NOT_FOUND, + }, + )?; - Ok(NamespaceProofQueryData { - transactions, - proof, - }) + Ok(NamespaceProofQueryData { + transactions: proof.export_all_txs(&ns_id), + proof: Some(proof), + }) + } else { + // ns_id not found in ns_table + Ok(NamespaceProofQueryData { + proof: None, + transactions: Vec::new(), + }) + } } .boxed() })?; @@ -138,6 +121,20 @@ where Ok(api) } +type ExplorerApi = Api, explorer::Error, Ver>; + +pub(super) fn explorer( + bind_version: Ver, +) -> Result> +where + N: network::Type, + D: ExplorerStorage + Send + Sync + 'static, + P: SequencerPersistence, +{ + let api = explorer::define_api::, SeqTypes, Ver>(bind_version)?; + Ok(api) +} + type NodeApi = Api, node::Error, Ver>; pub(super) fn node( @@ -164,14 +161,15 @@ where let toml = toml::from_str::(include_str!("../../api/submit.toml"))?; let mut api = Api::::new(toml)?; - api.post("submit", |req, state| { + api.at("submit", |req, state| { async move { let tx = req .body_auto::(Ver::instance()) .map_err(Error::from_request_error)?; + let hash = tx.commit(); state - .submit(tx) + .read(|state| state.submit(tx).boxed()) .await .map_err(|err| Error::internal(err.to_string()))?; Ok(hash) @@ -202,7 +200,7 @@ where .get_state_signature(height) .await .ok_or(tide_disco::Error::catch_all( - StatusCode::NotFound, + StatusCode::NOT_FOUND, "Signature not found.".to_owned(), )) } @@ -215,69 +213,62 @@ where pub(super) fn catchup(_: Ver) -> Result> where S: 'static + Send + Sync + ReadState, - S::State: Send + Sync + StateDataSource, + S::State: Send + Sync + CatchupDataSource, { let toml = toml::from_str::(include_str!("../../api/catchup.toml"))?; let mut api = Api::::new(toml)?; - async fn get_state( - req: &tide_disco::RequestParams, - state: &S, - ) -> Result, Error> { - match req - .opt_integer_param("view") - .map_err(Error::from_request_error)? - { - Some(view) => state - .get_undecided_state(ViewNumber::new(view)) - .await - .ok_or(Error::catch_all( - StatusCode::NotFound, - format!("state not available for view {view}"), - )), - None => Ok(state.get_decided_state().await), - } - } - api.get("account", |req, state| { async move { - let state = get_state(&req, state).await?; + let height = req + .integer_param("height") + .map_err(Error::from_request_error)?; + let view = req + .integer_param("view") + .map_err(Error::from_request_error)?; let account = req .string_param("address") .map_err(Error::from_request_error)?; let account = account.parse().map_err(|err| { Error::catch_all( - StatusCode::BadRequest, + StatusCode::BAD_REQUEST, format!("malformed account {account}: {err}"), ) })?; - let (proof, balance) = - FeeAccountProof::prove(&state.fee_merkle_tree, account).ok_or(Error::catch_all( - StatusCode::NotFound, - format!("account {account} is not in memory"), - ))?; - Ok(AccountQueryData { balance, proof }) + state + .get_account(height, ViewNumber::new(view), account) + .await + .map_err(|err| Error::catch_all(StatusCode::NOT_FOUND, format!("{err:#}"))) } .boxed() })? .get("blocks", |req, state| { async move { - let state = get_state(&req, state).await?; - - // Get the frontier of the blocks Merkle tree, if we have it. - let tree = &state.block_merkle_tree; - let frontier: BlocksFrontier = tree - .lookup(tree.num_leaves() - 1) - .expect_ok() - .map_err(|err| { - Error::catch_all( - StatusCode::NotFound, - format!("blocks frontier is not in memory: {err}"), - ) - })? - .1; - Ok(frontier) + let height = req + .integer_param("height") + .map_err(Error::from_request_error)?; + let view = req + .integer_param("view") + .map_err(Error::from_request_error)?; + + state + .get_frontier(height, ViewNumber::new(view)) + .await + .map_err(|err| Error::catch_all(StatusCode::NOT_FOUND, format!("{err:#}"))) + } + .boxed() + })? + .get("chainconfig", |req, state| { + async move { + let commitment = req + .blob_param("commitment") + .map_err(Error::from_request_error)?; + + state + .get_chain_config(commitment) + .await + .map_err(|err| Error::catch_all(StatusCode::NOT_FOUND, format!("{err:#}"))) } .boxed() })?; @@ -291,7 +282,11 @@ pub(super) fn merklized_state Result> where N: network::Type, - D: MerklizedStateDataSource + Send + Sync + 'static, + D: MerklizedStateDataSource + + Send + + Sync + + MerklizedStateHeightPersistence + + 'static, S: MerklizedState, P: SequencerPersistence, for<'a> >::Error: std::fmt::Display, @@ -301,3 +296,51 @@ where )?; Ok(api) } + +pub(super) fn config(_: Ver) -> Result> +where + S: 'static + Send + Sync + ReadState, + S::State: Send + Sync + HotShotConfigDataSource, +{ + let toml = toml::from_str::(include_str!("../../api/config.toml"))?; + let mut api = Api::::new(toml)?; + + let env_variables = get_public_env_vars() + .map_err(|err| Error::catch_all(StatusCode::INTERNAL_SERVER_ERROR, format!("{err:#}")))?; + + api.get("hotshot", |_, state| { + async move { Ok(state.get_config().await) }.boxed() + })? + .get("env", move |_, _| { + { + let env_variables = env_variables.clone(); + async move { Ok(env_variables) } + } + .boxed() + })?; + + Ok(api) +} + +fn get_public_env_vars() -> Result> { + let toml: toml::Value = toml::from_str(include_str!("../../api/public-env-vars.toml"))?; + + let keys = toml + .get("variables") + .ok_or_else(|| toml::de::Error::custom("variables not found"))? + .as_array() + .ok_or_else(|| toml::de::Error::custom("variables is not an array"))? + .clone() + .into_iter() + .map(|v| v.try_into()) + .collect::, toml::de::Error>>()?; + + let hashmap: HashMap = env::vars().collect(); + let mut public_env_vars: Vec = Vec::new(); + for key in keys { + let value = hashmap.get(&key).cloned().unwrap_or_default(); + public_env_vars.push(format!("{key}={value}")); + } + + Ok(public_env_vars) +} diff --git a/sequencer/src/api/fs.rs b/sequencer/src/api/fs.rs index c9be89779..ce184ba65 100644 --- a/sequencer/src/api/fs.rs +++ b/sequencer/src/api/fs.rs @@ -1,4 +1,4 @@ -use super::data_source::{Provider, SequencerDataSource}; +use super::data_source::{CatchupDataSource, Provider, SequencerDataSource}; use crate::{persistence::fs::Options, SeqTypes}; use async_trait::async_trait; use hotshot_query_service::data_source::FileSystemDataSource; @@ -11,7 +11,7 @@ impl SequencerDataSource for DataSource { type Options = Options; async fn create(opt: Self::Options, provider: Provider, reset: bool) -> anyhow::Result { - let path = Path::new(&opt.path); + let path = Path::new(opt.path()); let data_source = { if reset { FileSystemDataSource::create(path, provider).await? @@ -24,40 +24,28 @@ impl SequencerDataSource for DataSource { } } +impl CatchupDataSource for DataSource {} + #[cfg(test)] mod impl_testable_data_source { use super::*; - use crate::{ - api::{self, data_source::testing::TestableSequencerDataSource}, - persistence::{fs, PersistenceOptions}, - }; + use crate::api::{self, data_source::testing::TestableSequencerDataSource}; use tempfile::TempDir; #[async_trait] impl TestableSequencerDataSource for DataSource { type Storage = TempDir; - type Persistence = fs::Persistence; async fn create_storage() -> Self::Storage { TempDir::new().unwrap() } - async fn connect(storage: &Self::Storage) -> Self::Persistence { - Options { - path: storage.path().into(), - } - .create() - .await - .unwrap() + fn persistence_options(storage: &Self::Storage) -> Self::Options { + Options::new(storage.path().into()) } fn options(storage: &Self::Storage, opt: api::Options) -> api::Options { - opt.query_fs( - Default::default(), - Options { - path: storage.path().into(), - }, - ) + opt.query_fs(Default::default(), Options::new(storage.path().into())) } } } diff --git a/sequencer/src/api/options.rs b/sequencer/src/api/options.rs index 46519e0de..3a99b5c86 100644 --- a/sequencer/src/api/options.rs +++ b/sequencer/src/api/options.rs @@ -2,7 +2,8 @@ use super::{ data_source::{ - provider, SequencerDataSource, StateDataSource, StateSignatureDataSource, SubmitDataSource, + provider, CatchupDataSource, HotShotConfigDataSource, SequencerDataSource, + StateSignatureDataSource, SubmitDataSource, }, endpoints, fs, sql, update::update_loop, @@ -19,7 +20,7 @@ use async_std::sync::{Arc, RwLock}; use clap::Parser; use futures::{ channel::oneshot, - future::{BoxFuture, FutureExt}, + future::{BoxFuture, Future, FutureExt}, }; use hotshot_query_service::{ data_source::{ExtensibleDataSource, MetricsDataSource}, @@ -28,6 +29,7 @@ use hotshot_query_service::{ }; use hotshot_types::traits::metrics::{Metrics, NoMetrics}; use tide_disco::{ + listener::RateLimitListener, method::{ReadState, WriteState}, App, Url, }; @@ -42,8 +44,10 @@ pub struct Options { pub submit: Option, pub status: Option, pub catchup: Option, + pub config: Option, pub state: Option, pub hotshot_events: Option, + pub explorer: Option, pub storage_fs: Option, pub storage_sql: Option, } @@ -56,8 +60,10 @@ impl From for Options { submit: None, status: None, catchup: None, + config: None, state: None, hotshot_events: None, + explorer: None, storage_fs: None, storage_sql: None, } @@ -65,6 +71,11 @@ impl From for Options { } impl Options { + /// Default options for running a web server on the given port. + pub fn with_port(port: u16) -> Self { + Http::with_port(port).into() + } + /// Add a query API module backed by a Postgres database. pub fn query_sql(mut self, query: Query, storage: persistence::sql::Options) -> Self { self.query = Some(query); @@ -97,6 +108,12 @@ impl Options { self } + /// Add a config API module. + pub fn config(mut self, opt: Config) -> Self { + self.config = Some(opt); + self + } + /// Add a state API module. pub fn state(mut self, opt: State) -> Self { self.state = Some(opt); @@ -109,6 +126,12 @@ impl Options { self } + /// Add an explorer API module. + pub fn explorer(mut self, opt: Explorer) -> Self { + self.explorer = Some(opt); + self + } + /// Whether these options will run the query API. pub fn has_query_module(&self) -> bool { self.query.is_some() && (self.storage_fs.is_some() || self.storage_sql.is_some()) @@ -164,7 +187,7 @@ impl Options { ) .await? } else if let Some(opt) = self.storage_fs.take() { - self.init_with_query_module_fs::( + self.init_with_query_module_fs::( query_opt, opt, state, @@ -198,10 +221,7 @@ impl Options { )?; } - tasks.spawn( - "API server", - app.serve(format!("0.0.0.0:{}", self.http.port), bind_version), - ); + tasks.spawn("API server", self.listen(self.http.port, app, bind_version)); metrics } else { @@ -223,10 +243,7 @@ impl Options { )?; } - tasks.spawn( - "API server", - app.serve(format!("0.0.0.0:{}", self.http.port), bind_version), - ); + tasks.spawn("API server", self.listen(self.http.port, app, bind_version)); Box::new(NoMetrics) }; @@ -248,7 +265,7 @@ impl Options { where N: network::Type, P: SequencerPersistence, - D: SequencerDataSource + Send + Sync + 'static, + D: SequencerDataSource + CatchupDataSource + Send + Sync + 'static, { let metrics = ds.populate_metrics(); let ds: endpoints::AvailState = @@ -278,10 +295,10 @@ impl Options { Ok((metrics, ds, app)) } - async fn init_with_query_module_fs( + async fn init_with_query_module_fs( &self, query_opt: Query, - mod_opt: D::Options, + mod_opt: persistence::fs::Options, state: ApiState, tasks: &mut TaskList, bind_version: Ver, @@ -289,9 +306,13 @@ impl Options { where N: network::Type, P: SequencerPersistence, - D: SequencerDataSource + Send + Sync + 'static, { - let ds = D::create(mod_opt, provider(query_opt.peers, bind_version), false).await?; + let ds = ::create( + mod_opt, + provider(query_opt.peers, bind_version), + false, + ) + .await?; let (metrics, _, app) = self .init_app_modules(ds, state.clone(), tasks, bind_version) @@ -303,7 +324,7 @@ impl Options { tasks.spawn( "API server", - app.serve(format!("0.0.0.0:{}", self.http.port), Ver::instance()), + self.listen(self.http.port, app, Ver::instance()), ); Ok(metrics) } @@ -330,6 +351,10 @@ impl Options { .init_app_modules(ds, state.clone(), tasks, bind_version) .await?; + if self.explorer.is_some() { + app.register_module("explorer", endpoints::explorer(bind_version)?)?; + } + if self.state.is_some() { // Initialize merklized state module for block merkle tree app.register_module( @@ -346,7 +371,7 @@ impl Options { let get_node_state = async move { state.node_state().await.clone() }; tasks.spawn( "merklized state storage update loop", - update_state_storage_loop(ds, get_node_state), + update_state_storage_loop(ds, get_node_state, Ver::version()), ); } @@ -356,7 +381,7 @@ impl Options { tasks.spawn( "API server", - app.serve(format!("0.0.0.0:{}", self.http.port), Ver::instance()), + self.listen(self.http.port, app, Ver::instance()), ); Ok(metrics) } @@ -373,8 +398,12 @@ impl Options { where S: 'static + Send + Sync + ReadState + WriteState, P: SequencerPersistence, - S::State: - Send + Sync + SubmitDataSource + StateSignatureDataSource + StateDataSource, + S::State: Send + + Sync + + SubmitDataSource + + StateSignatureDataSource + + CatchupDataSource + + HotShotConfigDataSource, N: network::Type, { let bind_version = Ver::instance(); @@ -394,6 +423,10 @@ impl Options { let state_signature_api = endpoints::state_signature(bind_version)?; app.register_module("state-signature", state_signature_api)?; + if self.config.is_some() { + app.register_module("config", endpoints::config(bind_version)?)?; + } + Ok(()) } @@ -427,28 +460,68 @@ impl Options { tasks.spawn( "Hotshot Events Streaming API server", - app.serve( - format!( - "0.0.0.0:{}", - self.hotshot_events.unwrap().events_service_port - ), + self.listen( + self.hotshot_events.unwrap().events_service_port, + app, bind_version, ), ); Ok(()) } + + fn listen( + &self, + port: u16, + app: App, + bind_version: Ver, + ) -> impl Future> + where + S: Send + Sync + 'static, + E: Send + Sync + tide_disco::Error, + Ver: StaticVersionType + 'static, + { + let max_connections = self.http.max_connections; + + async move { + if let Some(limit) = max_connections { + app.serve(RateLimitListener::with_port(port, limit), bind_version) + .await?; + } else { + app.serve(format!("0.0.0.0:{}", port), bind_version).await?; + } + Ok(()) + } + } } /// The minimal HTTP API. /// /// The API automatically includes health and version endpoints. Additional API modules can be /// added by including the query-api or submit-api modules. -#[derive(Parser, Clone, Debug)] +#[derive(Parser, Clone, Copy, Debug)] pub struct Http { /// Port that the HTTP API will use. #[clap(long, env = "ESPRESSO_SEQUENCER_API_PORT")] pub port: u16, + + /// Maximum number of concurrent HTTP connections the server will allow. + /// + /// Connections exceeding this will receive and immediate 429 response and be closed. + /// + /// Leave unset for no connection limit. + #[clap(long, env = "ESPRESSO_SEQUENCER_MAX_CONNECTIONS")] + pub max_connections: Option, +} + +impl Http { + /// Default options for running a web server on the given port. + pub fn with_port(port: u16) -> Self { + Self { + port, + max_connections: None, + } + } } /// Options for the submission API module. @@ -463,6 +536,10 @@ pub struct Status; #[derive(Parser, Clone, Copy, Debug, Default)] pub struct Catchup; +/// Options for the config API module. +#[derive(Parser, Clone, Copy, Debug, Default)] +pub struct Config; + /// Options for the query API module. #[derive(Parser, Clone, Debug, Default)] pub struct Query { @@ -482,3 +559,7 @@ pub struct HotshotEvents { #[clap(long, env = "ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT")] pub events_service_port: u16, } + +/// Options for the explorer API module. +#[derive(Parser, Clone, Copy, Debug, Default)] +pub struct Explorer; diff --git a/sequencer/src/api/sql.rs b/sequencer/src/api/sql.rs index 1c3e7130f..6b9afac5f 100644 --- a/sequencer/src/api/sql.rs +++ b/sequencer/src/api/sql.rs @@ -1,7 +1,30 @@ -use super::data_source::{Provider, SequencerDataSource}; -use crate::{persistence::sql::Options, SeqTypes}; +use super::{ + data_source::{CatchupDataSource, Provider, SequencerDataSource}, + AccountQueryData, BlocksFrontier, +}; +use crate::{ + persistence::{ + sql::{sql_param, transaction, Options}, + ChainConfigPersistence, + }, + state::{BlockMerkleTree, FeeAccountProof, FeeMerkleTree}, + ChainConfig, SeqTypes, +}; +use anyhow::{bail, Context}; use async_trait::async_trait; -use hotshot_query_service::data_source::sql::{Config, SqlDataSource}; +use committable::Commitment; +use ethers::prelude::Address; +use futures::FutureExt; +use hotshot_query_service::{ + data_source::{ + sql::{Config, Query, SqlDataSource}, + storage::SqlStorage, + }, + merklized_state::{MerklizedStateDataSource, Snapshot}, + Resolvable, +}; +use hotshot_types::data::ViewNumber; +use jf_merkle_tree::{prelude::MerkleNode, MerkleTreeScheme}; pub type DataSource = SqlDataSource; @@ -10,23 +33,160 @@ impl SequencerDataSource for DataSource { type Options = Options; async fn create(opt: Self::Options, provider: Provider, reset: bool) -> anyhow::Result { + let fetch_limit = opt.fetch_rate_limit; + let active_fetch_delay = opt.active_fetch_delay; + let chunk_fetch_delay = opt.chunk_fetch_delay; let mut cfg = Config::try_from(opt)?; + if reset { cfg = cfg.reset_schema(); } - Ok(cfg.connect(provider).await?) + let mut builder = cfg.builder(provider).await?; + + if let Some(limit) = fetch_limit { + builder = builder.with_rate_limit(limit); + } + if let Some(delay) = active_fetch_delay { + builder = builder.with_active_fetch_delay(delay); + } + if let Some(delay) = chunk_fetch_delay { + builder = builder.with_chunk_fetch_delay(delay); + } + + builder.build().await + } +} + +impl CatchupDataSource for SqlStorage { + async fn get_account( + &self, + height: u64, + _view: ViewNumber, + account: Address, + ) -> anyhow::Result { + let proof = self + .get_path( + Snapshot::::Index(height), + account.into(), + ) + .await + .context(format!("fetching account {account}; height {height}"))?; + + match proof.proof.first().context(format!( + "empty proof for account {account}; height {height}" + ))? { + MerkleNode::Leaf { pos, elem, .. } => Ok(AccountQueryData { + balance: (*elem).into(), + proof: FeeAccountProof::presence(*pos, proof), + }), + + MerkleNode::Empty => Ok(AccountQueryData { + balance: 0_u64.into(), + proof: FeeAccountProof::absence(account.into(), proof), + }), + _ => { + bail!("Invalid proof"); + } + } + } + + async fn get_frontier(&self, height: u64, _view: ViewNumber) -> anyhow::Result { + self.get_path( + Snapshot::::Index(height), + height - 1, + ) + .await + .context(format!("fetching frontier at height {height}")) + } + + async fn get_chain_config( + &self, + commitment: committable::Commitment, + ) -> anyhow::Result { + self.load_chain_config(commitment).await + } +} + +impl CatchupDataSource for DataSource { + async fn get_account( + &self, + height: u64, + view: ViewNumber, + account: Address, + ) -> anyhow::Result { + (*self.storage().await) + .get_account(height, view, account) + .await + } + + async fn get_frontier(&self, height: u64, view: ViewNumber) -> anyhow::Result { + self.storage().await.get_frontier(height, view).await + } +} + +#[async_trait] +impl ChainConfigPersistence for SqlStorage { + async fn insert_chain_config(&mut self, chain_config: ChainConfig) -> anyhow::Result<()> { + let commitment = chain_config.commitment(); + let data = bincode::serialize(&chain_config)?; + + transaction(self, |mut tx| { + async move { + tx.upsert( + "chain_config", + ["commitment", "data"], + ["commitment"], + [[sql_param(&(commitment.to_string())), sql_param(&data)]], + ) + .await + .map_err(Into::into) + } + .boxed() + }) + .await + } + + async fn load_chain_config( + &self, + commitment: committable::Commitment, + ) -> anyhow::Result { + let query = self + .query_one( + "SELECT * from chain_config where commitment = $1", + [&commitment.to_string()], + ) + .await?; + + let data: Vec = query.try_get("data")?; + + bincode::deserialize(&data[..]).context("failed to deserialize") + } +} + +#[async_trait] +impl ChainConfigPersistence for DataSource { + async fn insert_chain_config(&mut self, chain_config: ChainConfig) -> anyhow::Result<()> { + (*self.storage_mut().await) + .insert_chain_config(chain_config) + .await + } + + async fn load_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + self.storage().await.load_chain_config(commitment).await } } #[cfg(test)] mod impl_testable_data_source { + use super::*; - use crate::{ - api::{self, data_source::testing::TestableSequencerDataSource}, - persistence::PersistenceOptions, - }; - use hotshot_query_service::data_source::storage::sql::{testing::TmpDb, SqlStorage}; + use crate::api::{self, data_source::testing::TestableSequencerDataSource}; + + use hotshot_query_service::data_source::storage::sql::testing::TmpDb; fn tmp_options(db: &TmpDb) -> Options { Options { @@ -41,14 +201,13 @@ mod impl_testable_data_source { #[async_trait] impl TestableSequencerDataSource for DataSource { type Storage = TmpDb; - type Persistence = SqlStorage; async fn create_storage() -> Self::Storage { TmpDb::init().await } - async fn connect(storage: &Self::Storage) -> Self::Persistence { - tmp_options(storage).create().await.unwrap() + fn persistence_options(storage: &Self::Storage) -> Self::Options { + tmp_options(storage) } fn options(storage: &Self::Storage, opt: api::Options) -> api::Options { diff --git a/sequencer/src/bin/cdn-broker.rs b/sequencer/src/bin/cdn-broker.rs index 8720cf203..0e6266261 100644 --- a/sequencer/src/bin/cdn-broker.rs +++ b/sequencer/src/bin/cdn-broker.rs @@ -1,12 +1,13 @@ //! The following is the main `Broker` binary, which just instantiates and runs //! a `Broker` object. -use anyhow::Result; +use anyhow::{Context, Result}; use cdn_broker::reexports::crypto::signature::KeyPair; use cdn_broker::{Broker, Config}; use clap::Parser; use hotshot_types::traits::node_implementation::NodeType; use hotshot_types::traits::signature_key::SignatureKey; use sequencer::network::cdn::{ProductionDef, WrappedSignatureKey}; +use sequencer::options::parse_size; use sequencer::SeqTypes; use sha2::Digest; use tracing_subscriber::EnvFilter; @@ -70,8 +71,19 @@ struct Args { ca_key_path: Option, /// The seed for broker key generation - #[arg(short, long, default_value_t = 0)] + #[arg(short, long, default_value_t = 0, env = "ESPRESSO_CDN_BROKER_KEY_SEED")] key_seed: u64, + + /// The size of the global memory pool. This is the maximum number of bytes that + /// can be allocated at once for all connections. A connection will block if it + /// tries to allocate more than this amount until some memory is freed. + #[arg( + long, + default_value = "1GB", + value_parser = parse_size, + env = "ESPRESSO_CDN_BROKER_GLOBAL_MEMORY_POOL_SIZE" + )] + global_memory_pool_size: u64, } #[async_std::main] async fn main() -> Result<()> { @@ -95,6 +107,15 @@ async fn main() -> Result<()> { let (public_key, private_key) = ::SignatureKey::generated_from_seed_indexed(key_hash.into(), 1337); + // Cast the memory pool size to a `usize` + let global_memory_pool_size = + usize::try_from(args.global_memory_pool_size).with_context(|| { + format!( + "Failed to convert global memory pool size to usize: {}", + args.global_memory_pool_size + ) + })?; + // Create config let broker_config: Config> = Config { ca_cert_path: args.ca_cert_path, @@ -111,6 +132,7 @@ async fn main() -> Result<()> { public_advertise_endpoint: args.public_advertise_endpoint, private_bind_endpoint: args.private_bind_endpoint, private_advertise_endpoint: args.private_advertise_endpoint, + global_memory_pool_size: Some(global_memory_pool_size), }; // Create new `Broker` diff --git a/sequencer/src/bin/cdn-marshal.rs b/sequencer/src/bin/cdn-marshal.rs index a9646d8b7..7b15f2378 100644 --- a/sequencer/src/bin/cdn-marshal.rs +++ b/sequencer/src/bin/cdn-marshal.rs @@ -1,10 +1,10 @@ //! The following is the main `Marshal` binary, which just instantiates and runs //! a `Marshal` object. -use anyhow::Result; +use anyhow::{Context, Result}; use cdn_marshal::{Config, Marshal}; use clap::Parser; -use sequencer::{network::cdn::ProductionDef, SeqTypes}; +use sequencer::{network::cdn::ProductionDef, options::parse_size, SeqTypes}; use tracing_subscriber::EnvFilter; #[derive(Parser, Debug)] @@ -38,6 +38,17 @@ struct Args { /// If not provided, a local, pinned CA is used #[arg(long, env = "ESPRESSO_CDN_MARSHAL_CA_KEY_PATH")] ca_key_path: Option, + + /// The size of the global memory pool. This is the maximum number of bytes that + /// can be allocated at once for all connections. A connection will block if it + /// tries to allocate more than this amount until some memory is freed. + #[arg( + long, + default_value = "1GB", + value_parser = parse_size, + env = "ESPRESSO_CDN_MARSHAL_GLOBAL_MEMORY_POOL_SIZE" + )] + global_memory_pool_size: u64, } #[async_std::main] @@ -57,6 +68,15 @@ async fn main() -> Result<()> { .init(); } + // Cast the memory pool size to a `usize` + let global_memory_pool_size = + usize::try_from(args.global_memory_pool_size).with_context(|| { + format!( + "Failed to convert global memory pool size to usize: {}", + args.global_memory_pool_size + ) + })?; + // Create a new `Config` let config = Config { discovery_endpoint: args.discovery_endpoint, @@ -64,6 +84,7 @@ async fn main() -> Result<()> { metrics_bind_endpoint: args.metrics_bind_endpoint, ca_cert_path: args.ca_cert_path, ca_key_path: args.ca_key_path, + global_memory_pool_size: Some(global_memory_pool_size), }; // Create new `Marshal` from the config diff --git a/sequencer/src/bin/deploy.rs b/sequencer/src/bin/deploy.rs index 022265034..7a508c132 100644 --- a/sequencer/src/bin/deploy.rs +++ b/sequencer/src/bin/deploy.rs @@ -1,18 +1,9 @@ -use anyhow::{ensure, Context}; use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; -use async_std::sync::Arc; use clap::Parser; -use contract_bindings::{ - erc1967_proxy::ERC1967Proxy, hot_shot::HotShot, light_client::LightClient, -}; -use ethers::prelude::{coins_bip39::English, *}; -use futures::future::FutureExt; +use futures::FutureExt; use hotshot_stake_table::config::STAKE_TABLE_CAPACITY; use hotshot_state_prover::service::light_client_genesis; -use sequencer_utils::deployer::{ - deploy_light_client_contract, deploy_mock_light_client_contract, Contract, Contracts, - DeployedContracts, -}; +use sequencer_utils::deployer::{deploy, ContractGroup, Contracts, DeployedContracts}; use std::{fs::File, io::stdout, path::PathBuf}; use url::Url; @@ -43,15 +34,14 @@ struct Options { )] rpc_url: Url, - /// URL of the HotShot orchestrator. - /// - /// This is used to get the stake table for initializing the light client contract. + /// URL of a sequencer node that is currently providing the HotShot config. + /// This is used to initialize the stake table. #[clap( long, - env = "ESPRESSO_SEQUENCER_ORCHESTRATOR_URL", - default_value = "http://localhost:40001" + env = "ESPRESSO_SEQUENCER_URL", + default_value = "http://localhost:24000" )] - orchestrator_url: Url, + pub sequencer_url: Url, /// Mnemonic for an L1 wallet. /// @@ -73,6 +63,10 @@ struct Options { )] account_index: u32, + /// Only deploy the given groups of related contracts. + #[clap(long, value_delimiter = ',')] + only: Option>, + /// Write deployment results to OUT as a .env file. /// /// If not provided, the results will be written to stdout. @@ -82,7 +76,7 @@ struct Options { #[clap(flatten)] contracts: DeployedContracts, - /// If toggled, launch a mock prover contract that does not do any proof verification. + /// If toggled, launch a mock prover contract with a smaller verification key. #[clap(short, long)] pub use_mock_contract: bool, @@ -97,58 +91,22 @@ async fn main() -> anyhow::Result<()> { setup_backtrace(); let opt = Options::parse(); - let mut contracts = Contracts::from(opt.contracts); - - let provider = Provider::::try_from(opt.rpc_url.to_string())?; - let chain_id = provider.get_chainid().await?.as_u64(); - let wallet = MnemonicBuilder::::default() - .phrase(opt.mnemonic.as_str()) - .index(opt.account_index)? - .build()? - .with_chain_id(chain_id); - let owner = wallet.address(); - let l1 = Arc::new(SignerMiddleware::new(provider, wallet)); - - // As a sanity check, check that the deployer address has some balance of ETH it can use to pay - // gas. - let balance = l1.get_balance(owner, None).await?; - ensure!( - balance > 0.into(), - "deployer account {owner:#x} is not funded!" - ); - tracing::info!(%balance, "deploying from address {owner:#x}"); - - contracts - .deploy_tx(Contract::HotShot, HotShot::deploy(l1.clone(), ())?) - .await?; - - // Deploy the upgradable light client contract first, then initialize it through a proxy contract - let lc_address = if opt.use_mock_contract { - contracts - .deploy_fn(Contract::LightClient, |contracts| { - deploy_mock_light_client_contract(l1.clone(), contracts, None).boxed() - }) - .await? - } else { - contracts - .deploy_fn(Contract::LightClient, |contracts| { - deploy_light_client_contract(l1.clone(), contracts).boxed() - }) - .await? - }; - let light_client = LightClient::new(lc_address, l1.clone()); - - let genesis = light_client_genesis(&opt.orchestrator_url, opt.stake_table_capacity).await?; - let data = light_client - .initialize(genesis.into(), u32::MAX, owner) - .calldata() - .context("calldata for initialize transaction not available")?; - contracts - .deploy_tx( - Contract::LightClientProxy, - ERC1967Proxy::deploy(l1.clone(), (lc_address, data))?, - ) - .await?; + let contracts = Contracts::from(opt.contracts); + + let sequencer_url = opt.sequencer_url.clone(); + + let genesis = light_client_genesis(&sequencer_url, opt.stake_table_capacity).boxed(); + + let contracts = deploy( + opt.rpc_url, + opt.mnemonic, + opt.account_index, + opt.use_mock_contract, + opt.only, + genesis, + contracts, + ) + .await?; if let Some(out) = &opt.out { let file = File::options() diff --git a/sequencer/src/bin/dev-cdn.rs b/sequencer/src/bin/dev-cdn.rs index 73def9e41..064142d71 100644 --- a/sequencer/src/bin/dev-cdn.rs +++ b/sequencer/src/bin/dev-cdn.rs @@ -75,6 +75,7 @@ async fn main() -> Result<()> { ca_cert_path: None, ca_key_path: None, + global_memory_pool_size: Some(1024 * 1024 * 1024), }; // Configure the marshal @@ -84,6 +85,7 @@ async fn main() -> Result<()> { discovery_endpoint: discovery_endpoint.clone(), ca_cert_path: None, ca_key_path: None, + global_memory_pool_size: Some(1024 * 1024 * 1024), }; // Create a new `Broker` diff --git a/sequencer/src/bin/espresso-bridge.rs b/sequencer/src/bin/espresso-bridge.rs new file mode 100644 index 000000000..63045b7b4 --- /dev/null +++ b/sequencer/src/bin/espresso-bridge.rs @@ -0,0 +1,343 @@ +use anyhow::{bail, ensure, Context}; +use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; +use async_std::{sync::Arc, task::sleep}; +use clap::Parser; +use contract_bindings::fee_contract::FeeContract; +use es_version::SequencerVersion; +use ethers::{ + middleware::{Middleware, SignerMiddleware}, + providers::Provider, + types::{Address, BlockId, U256}, +}; +use futures::stream::StreamExt; +use jf_merkle_tree::{ + prelude::{MerkleProof, Sha3Node}, + MerkleTreeScheme, +}; +use sequencer::{ + eth_signature_key::EthKeyPair, + state::{FeeAccount, FeeAmount, FeeMerkleTree}, + Header, +}; +use std::time::Duration; +use surf_disco::{error::ClientError, Url}; + +type EspressoClient = surf_disco::Client; + +type FeeMerkleProof = MerkleProof; + +/// Command-line utility for working with the Espresso bridge. +#[derive(Debug, Parser)] +enum Command { + Deposit(Deposit), + Balance(Balance), + L1Balance(L1Balance), +} + +/// Deposit ETH from the L1 into Espresso. +#[derive(Debug, Parser)] +struct Deposit { + /// L1 JSON-RPC provider. + #[clap(short, long, env = "L1_PROVIDER")] + rpc_url: Url, + + /// Espresso query service provider. + /// + /// This must point to an Espresso node running the /availability, /node and Merklized state + /// (/fee-state and /block-state) APIs. + #[clap(short, long, env = "ESPRESSO_PROVIDER")] + espresso_provider: Url, + + /// The address of the Espresso fee contract on the L1. + #[clap(short, long, env = "CONTRACT_ADDRESS")] + contract_address: Address, + + /// Mnemonic to generate the account from which to deposit. + #[clap(short, long, env = "MNEMONIC")] + mnemonic: String, + + /// Account index when deriving an account from MNEMONIC. + #[clap(short = 'i', long, env = "ACCOUNT_INDEX", default_value = "0")] + account_index: u32, + + /// Amount of WEI to deposit. + // Note: we use u64 because U256 parses in hex, which is annoying. We can easily convert to U256 + // after parsing. + #[clap(short, long, env = "AMOUNT")] + amount: u64, + + /// Number of confirmations to wait for before considering an L1 transaction mined. + #[clap(long, env = "CONFIRMATIONS", default_value = "6")] + confirmations: usize, +} + +/// Check the balance (in ETH) of an Espresso account. +#[derive(Debug, Parser)] +struct Balance { + /// Espresso query service provider. + /// + /// This must point to an Espresso node running the node and Merklized state APIs. + #[clap(short, long, env = "ESPRESSO_PROVIDER")] + espresso_provider: Url, + + /// Account to check. + #[clap(short, long, env = "ADDRESS", required_unless_present = "mnemonic")] + address: Option

, + + /// Mnemonic to generate the account to check. + #[clap(short, long, env = "MNEMONIC", conflicts_with = "address")] + mnemonic: Option, + + /// Account index when deriving an account from MNEMONIC. + #[clap( + short = 'i', + long, + env = "ACCOUNT_INDEX", + default_value = "0", + conflicts_with = "address" + )] + account_index: u32, + + /// Espresso block number at which to check (default: latest). + #[clap(short, long, env = "BLOCK")] + block: Option, +} + +/// Check the balance (in ETH) of an L1 account. +#[derive(Debug, Parser)] +struct L1Balance { + /// L1 JSON-RPC provider. + #[clap(short, long, env = "L1_PROVIDER")] + rpc_url: Url, + + /// Account to check. + #[clap(short, long, env = "ADDRESS", required_unless_present = "mnemonic")] + address: Option
, + + /// Mnemonic to generate the account to check. + #[clap(short, long, env = "MNEMONIC", conflicts_with = "address")] + mnemonic: Option, + + /// Account index when deriving an account from MNEMONIC. + #[clap( + short = 'i', + long, + env = "ACCOUNT_INDEX", + default_value = "0", + conflicts_with = "address" + )] + account_index: u32, + + /// L1 block number at which to check (default: latest). + #[clap(short, long, env = "BLOCK")] + block: Option, +} + +async fn deposit(opt: Deposit) -> anyhow::Result<()> { + // Derive the account to deposit from. + let key_pair = EthKeyPair::from_mnemonic(opt.mnemonic, opt.account_index)?; + + // Connect to L1. + let rpc = Provider::try_from(opt.rpc_url.to_string())?; + let signer = key_pair.signer(); + let l1 = Arc::new(SignerMiddleware::new_with_provider_chain(rpc, signer).await?); + let contract = FeeContract::new(opt.contract_address, l1.clone()); + + // Connect to Espresso. + let espresso = EspressoClient::new(opt.espresso_provider); + + // Validate deposit. + let amount = U256::from(opt.amount); + let min_deposit = contract.min_deposit_amount().call().await?; + let max_deposit = contract.max_deposit_amount().call().await?; + ensure!( + amount >= min_deposit, + "amount is too small (minimum deposit: {min_deposit})", + ); + ensure!( + amount <= max_deposit, + "amount is too large (maximum deposit: {max_deposit})", + ); + + // Record the initial balance on Espresso. + let initial_balance = get_espresso_balance(&espresso, l1.address(), None).await?; + tracing::debug!(%initial_balance, "initial balance"); + + // Send the deposit transaction. + tracing::info!(address = %l1.address(), %amount, "sending deposit transaction"); + let call = contract.deposit(l1.address()).value(amount); + let tx = call.send().await.context("sending deposit transaction")?; + tracing::info!(hash = %tx.tx_hash(), "deposit transaction sent to L1"); + + // Wait for the transaction to finalize on L1. + let receipt = tx + .confirmations(opt.confirmations) + .await + .context("waiting for deposit transaction")? + .context("deposit transaction not mined")?; + let l1_block = receipt + .block_number + .context("deposit transaction not mined")? + .as_u64(); + ensure!( + receipt.status == Some(1.into()), + "deposit transaction reverted" + ); + tracing::info!(l1_block, "deposit mined on L1"); + + // Wait for Espresso to catch up to the L1. + let espresso_height = espresso + .get::("node/block-height") + .send() + .await + .context("getting Espresso block height")?; + let mut headers = espresso + .socket(&format!("availability/stream/headers/{espresso_height}")) + .subscribe() + .await + .context("subscribing to Espresso headers")?; + let espresso_block = loop { + let header: Header = match headers.next().await.context("header stream ended")? { + Ok(header) => header, + Err(err) => { + tracing::warn!("error in header stream: {err:#}"); + continue; + } + }; + let Some(l1_finalized) = header.l1_finalized else { + continue; + }; + if l1_finalized.number >= l1_block { + tracing::info!(block = header.height, "deposit finalized on Espresso"); + break header.height; + } else { + tracing::debug!( + block = header.height, + l1_block, + ?l1_finalized, + "waiting for deposit on Espresso" + ) + } + }; + + // Confirm that the Espresso balance has increased. + let final_balance = get_espresso_balance(&espresso, l1.address(), Some(espresso_block)).await?; + if final_balance >= initial_balance + amount.into() { + tracing::info!(%final_balance, "deposit successful"); + } else { + // The balance didn't increase as much as expected. This doesn't necessarily mean the + // deposit failed: there could have been a race condition where the balance on Espresso was + // altered by some other operation at the same time, but we should at least let the user + // know about it. + tracing::warn!(%initial_balance, %final_balance, "Espresso balance did not increase as expected"); + } + + Ok(()) +} + +async fn balance(opt: Balance) -> anyhow::Result<()> { + // Derive the address to look up. + let address = if let Some(address) = opt.address { + address + } else if let Some(mnemonic) = opt.mnemonic { + EthKeyPair::from_mnemonic(mnemonic, opt.account_index)?.address() + } else { + bail!("address or mnemonic must be provided"); + }; + + let espresso = EspressoClient::new(opt.espresso_provider); + let balance = get_espresso_balance(&espresso, address, opt.block).await?; + + // Output the balance on regular standard out, rather than as a log message, to make scripting + // easier. + println!("{balance}"); + + Ok(()) +} + +async fn l1_balance(opt: L1Balance) -> anyhow::Result<()> { + // Derive the address to look up. + let address = if let Some(address) = opt.address { + address + } else if let Some(mnemonic) = opt.mnemonic { + EthKeyPair::from_mnemonic(mnemonic, opt.account_index)?.address() + } else { + bail!("address or mnemonic must be provided"); + }; + + let l1 = Provider::try_from(opt.rpc_url.to_string())?; + + let block = opt.block.map(BlockId::from); + tracing::debug!(%address, ?block, "fetching L1 balance"); + let balance = l1 + .get_balance(address, block) + .await + .context("getting account balance")?; + + // Output the balance on regular standard out, rather than as a log message, to make scripting + // easier. + println!("{balance}"); + + Ok(()) +} + +async fn get_espresso_balance( + espresso: &EspressoClient, + address: Address, + block: Option, +) -> anyhow::Result { + // Get the block height to query at, defaulting to the latest block. + let block = if let Some(block) = block { + block + } else { + espresso + .get::("node/block-height") + .send() + .await + .context("getting block height")? + - 1 + }; + + // Download the Merkle path for this fee account at the specified block height. Transient errors + // are possible (for example, if we are fetching from the latest block, the block height might + // get incremented slightly before the state becomes available) so retry a few times. + let mut retry = 0; + let max_retries = 5; + let proof = loop { + tracing::debug!(%address, block, retry, "fetching Espresso balance"); + match espresso + .get::(&format!("fee-state/{block}/{address:#x}")) + .send() + .await + { + Ok(proof) => break proof, + Err(err) => { + tracing::warn!("error getting account balance: {err:#}"); + retry += 1; + + if retry == max_retries { + return Err(err).context("getting account balance"); + } else { + sleep(Duration::from_secs(5)).await; + } + } + } + }; + + // If the element in the Merkle path is missing -- there is no account with this address -- the + // balance is defined to be 0. + let balance = proof.elem().copied().unwrap_or(0.into()); + Ok(balance) +} + +#[async_std::main] +async fn main() -> anyhow::Result<()> { + setup_logging(); + setup_backtrace(); + + match Command::parse() { + Command::Deposit(opt) => deposit(opt).await, + Command::Balance(opt) => balance(opt).await, + Command::L1Balance(opt) => l1_balance(opt).await, + } +} diff --git a/sequencer/src/bin/espresso-dev-node.rs b/sequencer/src/bin/espresso-dev-node.rs new file mode 100644 index 000000000..9bbf1dac9 --- /dev/null +++ b/sequencer/src/bin/espresso-dev-node.rs @@ -0,0 +1,400 @@ +use std::{sync::Arc, time::Duration}; + +use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; +use async_std::task::{sleep, spawn, spawn_blocking}; +use clap::Parser; +use es_version::{SequencerVersion, SEQUENCER_VERSION}; +use ethers::{ + providers::{Http, Middleware, Provider}, + signers::{coins_bip39::English, MnemonicBuilder, Signer}, +}; +use futures::FutureExt; +use hotshot_state_prover::service::{ + load_proving_key, one_honest_threshold, sync_state, StateProverConfig, +}; +use hotshot_types::traits::stake_table::{SnapshotVersion, StakeTableScheme}; +use portpicker::pick_unused_port; +use sequencer::{ + api::{ + options, + test_helpers::{TestNetwork, STAKE_TABLE_CAPACITY_FOR_TEST}, + }, + persistence, + state_signature::relay_server::run_relay_server, + testing::TestConfig, +}; +use sequencer_utils::{ + deployer::{deploy, Contract, Contracts}, + AnvilOptions, +}; +use surf_disco::Client; +use tide_disco::error::ServerError; +use url::Url; + +#[derive(Clone, Debug, Parser)] +struct Args { + /// A JSON-RPC endpoint for the L1 to deploy to. If this is not provided, an Avil node will be + /// launched automatically. + #[clap(short, long, env = "ESPRESSO_SEQUENCER_L1_PROVIDER")] + rpc_url: Option, + /// Mnemonic for an L1 wallet. + /// + /// This wallet is used to deploy the contracts, so the account indicated by ACCOUNT_INDEX must + /// be funded with with ETH. + #[clap( + long, + name = "MNEMONIC", + env = "ESPRESSO_SEQUENCER_ETH_MNEMONIC", + default_value = "test test test test test test test test test test test junk" + )] + mnemonic: String, + /// Account index in the L1 wallet generated by MNEMONIC to use when deploying the contracts. + #[clap( + long, + name = "ACCOUNT_INDEX", + env = "ESPRESSO_DEPLOYER_ACCOUNT_INDEX", + default_value = "0" + )] + account_index: u32, + + /// Port that the HTTP API will use. + #[clap(long, env = "ESPRESSO_SEQUENCER_API_PORT")] + sequencer_api_port: u16, + + /// Maximum concurrent connections allowed by the HTTP API server. + #[clap(long, env = "ESPRESSO_SEQUENCER_MAX_CONNECTIONS")] + sequencer_api_max_connections: Option, + + /// Port for connecting to the builder. + #[clap(short, long, env = "ESPRESSO_BUILDER_PORT")] + builder_port: Option, + + #[clap(flatten)] + sql: persistence::sql::Options, +} + +#[async_std::main] +async fn main() -> anyhow::Result<()> { + setup_logging(); + setup_backtrace(); + + let cli_params = Args::parse(); + let api_options = options::Options::from(options::Http { + port: cli_params.sequencer_api_port, + max_connections: cli_params.sequencer_api_max_connections, + }) + .status(Default::default()) + .state(Default::default()) + .submit(Default::default()) + .query_sql(Default::default(), cli_params.sql); + + let (url, _anvil) = if let Some(url) = cli_params.rpc_url { + (url, None) + } else { + tracing::warn!("L1 url is not provided. running an anvil node"); + let instance = AnvilOptions::default().spawn().await; + let url = instance.url(); + tracing::info!("l1 url: {}", url); + (url, Some(instance)) + }; + + let relay_server_port = pick_unused_port().unwrap(); + let relay_server_url: Url = format!("http://localhost:{}", relay_server_port) + .parse() + .unwrap(); + + let mut config = TestConfig::default_with_l1(url.clone()); + config.builder_port = cli_params.builder_port; + config.state_relay_url = Some(relay_server_url.clone()); + + let network = TestNetwork::new_with_config( + api_options, + [persistence::no_storage::Options; TestConfig::NUM_NODES], + config, + ) + .await; + + let config = network.cfg.hotshot_config(); + tracing::info!("Hotshot config {config:?}"); + + let contracts = Contracts::new(); + + tracing::info!("deploying the contracts"); + + let light_client_genesis = network.light_client_genesis(); + + let contracts = deploy( + url.clone(), + cli_params.mnemonic.clone(), + cli_params.account_index, + true, + None, + async { Ok(light_client_genesis) }.boxed(), + contracts, + ) + .await?; + + // Run the relay server + let st = network.cfg.stake_table(); + let total_stake = st.total_stake(SnapshotVersion::LastEpochStart).unwrap(); + spawn(run_relay_server( + None, + one_honest_threshold(total_stake), + format!("http://0.0.0.0:{relay_server_port}") + .parse() + .unwrap(), + SEQUENCER_VERSION, + )); + + // Run the prover service. These code are basically from `hotshot-state-prover`. The difference + // is that here we don't need to fetch the `stake table` from other entities. + // TODO: Remove the redundant code. + let proving_key = + spawn_blocking(move || Arc::new(load_proving_key(STAKE_TABLE_CAPACITY_FOR_TEST as usize))) + .await; + let relay_server_client = + Client::::new(relay_server_url.clone()); + + let provider = Provider::::try_from(url.to_string()).unwrap(); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + + let update_interval = Duration::from_secs(20); + let retry_interval = Duration::from_secs(2); + let config = StateProverConfig { + relay_server: relay_server_url, + update_interval, + retry_interval, + l1_provider: url, + light_client_address: contracts + .get_contract_address(Contract::LightClientProxy) + .unwrap(), + eth_signing_key: MnemonicBuilder::::default() + .phrase(cli_params.mnemonic.as_str()) + .index(cli_params.account_index) + .expect("error building wallet") + .build() + .expect("error opening wallet") + .with_chain_id(chain_id) + .signer() + .clone(), + sequencer_url: "http://localhost".parse().unwrap(), // This should not be used in dev-node + port: None, + stake_table_capacity: STAKE_TABLE_CAPACITY_FOR_TEST as usize, + }; + + loop { + if let Err(err) = sync_state(&st, proving_key.clone(), &relay_server_client, &config).await + { + tracing::error!("Cannot sync the light client state, will retry: {}", err); + sleep(retry_interval).await; + } else { + tracing::info!("Sleeping for {:?}", update_interval); + sleep(update_interval).await; + } + } +} + +#[cfg(test)] +mod tests { + use std::{process::Child, sync::Arc, time::Duration}; + + use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; + use async_std::{stream::StreamExt, task::sleep}; + use committable::{Commitment, Committable}; + use contract_bindings::light_client::LightClient; + use es_version::SequencerVersion; + use escargot::CargoBuild; + use ethers::types::{Address, U256}; + use futures::TryStreamExt; + use hotshot_query_service::{ + availability::{BlockQueryData, TransactionQueryData, VidCommonQueryData}, + data_source::sql::testing::TmpDb, + }; + use jf_merkle_tree::MerkleTreeScheme; + use portpicker::pick_unused_port; + use sequencer::{ + api::endpoints::NamespaceProofQueryData, state::BlockMerkleTree, Header, SeqTypes, + Transaction, + }; + use sequencer_utils::{init_signer, AnvilOptions}; + use surf_disco::Client; + use tide_disco::error::ServerError; + + const TEST_MNEMONIC: &str = "test test test test test test test test test test test junk"; + + pub struct BackgroundProcess(Child); + + impl Drop for BackgroundProcess { + fn drop(&mut self) { + self.0.kill().unwrap(); + } + } + + // If this test failed and you are doing changes on the following stuff, please + // sync your changes to [`espresso-sequencer-go`](https://github.com/EspressoSystems/espresso-sequencer-go) + // and open a PR. + // - APIs update + // - Types (like `Header`) update + #[async_std::test] + async fn dev_node_test() { + setup_logging(); + setup_backtrace(); + + let builder_port = pick_unused_port().unwrap(); + + let api_port = pick_unused_port().unwrap(); + + let instance = AnvilOptions::default().spawn().await; + let l1_url = instance.url(); + + let db = TmpDb::init().await; + let postgres_port = db.port(); + + let process = CargoBuild::new() + .bin("espresso-dev-node") + .features("testing") + .current_target() + .run() + .unwrap() + .command() + .env("ESPRESSO_SEQUENCER_L1_PROVIDER", l1_url.to_string()) + .env("ESPRESSO_BUILDER_PORT", builder_port.to_string()) + .env("ESPRESSO_SEQUENCER_API_PORT", api_port.to_string()) + .env("ESPRESSO_SEQUENCER_POSTGRES_HOST", "localhost") + .env("ESPRESSO_SEQUENCER_ETH_MNEMONIC", TEST_MNEMONIC) + .env("ESPRESSO_DEPLOYER_ACCOUNT_INDEX", "0") + .env( + "ESPRESSO_SEQUENCER_POSTGRES_PORT", + postgres_port.to_string(), + ) + .env("ESPRESSO_SEQUENCER_POSTGRES_USER", "postgres") + .env("ESPRESSO_SEQUENCER_POSTGRES_PASSWORD", "password") + .spawn() + .unwrap(); + + let _process = BackgroundProcess(process); + + let api_client: Client = + Client::new(format!("http://localhost:{api_port}").parse().unwrap()); + api_client.connect(None).await; + + tracing::info!("waiting for blocks"); + let _ = api_client + .socket("availability/stream/blocks/0") + .subscribe::>() + .await + .unwrap() + .take(5) + .try_collect::>() + .await + .unwrap(); + + let builder_api_client: Client = + Client::new(format!("http://localhost:{builder_port}").parse().unwrap()); + builder_api_client.connect(None).await; + + let builder_address = builder_api_client + .get::("block_info/builderaddress") + .send() + .await + .unwrap(); + + assert!(!builder_address.is_empty()); + + let tx = Transaction::new(100.into(), vec![1, 2, 3]); + + let hash: Commitment = api_client + .post("submit/submit") + .body_json(&tx) + .unwrap() + .send() + .await + .unwrap(); + + let tx_hash = tx.commit(); + assert_eq!(hash, tx_hash); + + let mut tx_result = api_client + .get::>(&format!( + "availability/transaction/hash/{tx_hash}", + )) + .send() + .await; + while tx_result.is_err() { + sleep(Duration::from_secs(3)).await; + + tx_result = api_client + .get::>(&format!( + "availability/transaction/hash/{}", + tx_hash + )) + .send() + .await; + } + + let tx_block_height = tx_result.unwrap().block_height(); + + let light_client_address = "0xdc64a140aa3e981100a9beca4e685f962f0cf6c9"; + + let signer = init_signer(&l1_url, TEST_MNEMONIC, 0).await.unwrap(); + let light_client = LightClient::new( + light_client_address.parse::
().unwrap(), + Arc::new(signer), + ); + + while light_client + .get_hot_shot_commitment(U256::from(1)) + .call() + .await + .is_err() + { + tracing::info!("waiting for commitment"); + sleep(Duration::from_secs(3)).await; + } + + // Check the namespace proof + let proof = api_client + .get::(&format!( + "availability/block/{tx_block_height}/namespace/100" + )) + .send() + .await + .unwrap(); + assert!(proof.proof.is_some()); + + // These endpoints are currently used in `espresso-sequencer-go`. These checks + // serve as reminders of syncing the API updates to go client repo when they change. + { + api_client + .get::("status/block-height") + .send() + .await + .unwrap(); + + api_client + .get::
("availability/header/3") + .send() + .await + .unwrap(); + + api_client + .get::>(&format!( + "availability/vid/common/{tx_block_height}" + )) + .send() + .await + .unwrap(); + + while api_client + .get::<::MembershipProof>("block-state/3/2") + .send() + .await + .is_err() + { + sleep(Duration::from_secs(3)).await; + } + } + + drop(db); + } +} diff --git a/sequencer/src/bin/nasty-client.rs b/sequencer/src/bin/nasty-client.rs index cbf8c24c6..f752ba18d 100644 --- a/sequencer/src/bin/nasty-client.rs +++ b/sequencer/src/bin/nasty-client.rs @@ -31,14 +31,16 @@ use hotshot_query_service::{ metrics::PrometheusMetrics, node::TimeWindowQueryData, }; -use hotshot_types::{ - traits::metrics::{Counter, Gauge, Metrics as _}, - vid::{vid_scheme, VidSchemeType}, +use hotshot_types::traits::metrics::{Counter, Gauge, Histogram, Metrics as _}; +use jf_merkle_tree::{ + ForgetableMerkleTreeScheme, MerkleCommitment, MerkleTreeScheme, UniversalMerkleTreeScheme, }; -use jf_primitives::vid::VidScheme; use rand::{seq::SliceRandom, RngCore}; use sequencer::{ - api::endpoints::NamespaceProofQueryData, options::parse_duration, Header, SeqTypes, + api::endpoints::NamespaceProofQueryData, + options::parse_duration, + state::{BlockMerkleTree, FeeMerkleTree}, + Header, SeqTypes, }; use serde::de::DeserializeOwned; use std::{ @@ -50,7 +52,7 @@ use std::{ time::{Duration, Instant}, }; use strum::{EnumDiscriminants, VariantArray}; -use surf_disco::{error::ClientError, socket, Url}; +use surf_disco::{error::ClientError, socket, Error, StatusCode, Url}; use tide_disco::{error::ServerError, App}; use time::OffsetDateTime; use toml::toml; @@ -59,18 +61,6 @@ use tracing::info_span; /// An adversarial stress test for sequencer APIs. #[derive(Clone, Debug, Parser)] struct Options { - /// Timeout for HTTP requests. - /// - /// Requests that take longer than this will fail, causing an error log and an increment of the - /// `failed_actions` metric. - #[clap( - long, - env = "ESPRESS_NASTY_CLIENT_HTTP_TIMEOUT", - default_value = "30s", - value_parser = parse_duration, - )] - http_timeout: Duration, - /// Port on which to serve the nasty-client API. #[clap( short, @@ -93,6 +83,30 @@ struct Options { #[derive(Clone, Copy, Debug, Parser)] struct ClientConfig { + /// Timeout for HTTP requests. + /// + /// Requests that take longer than this will fail, causing an error log and an increment of the + /// `failed_actions` metric. + #[clap( + long, + env = "ESPRESSO_NASTY_CLIENT_HTTP_TIMEOUT_ERROR", + default_value = "5s", + value_parser = parse_duration, + )] + http_timeout_error: Duration, + + /// Timeout for issuing a warning due to slow HTTP requests. + /// + /// Requests that take longer than this but shorter than HTTP_TIMEOUT_ERROR will not generate an + /// error but will output a warning and increment a counter of slow HTTP requests. + #[clap( + long, + env = "ESPRESSO_NASTY_CLIENT_HTTP_TIMEOUT_WARNING", + default_value = "1s", + value_parser = parse_duration, + )] + http_timeout_warning: Duration, + /// The maximum number of open WebSockets connections for each resource type at any time. #[clap( long, @@ -128,6 +142,10 @@ struct ClientConfig { /// Time after which WebSockets connection failures are allowed. /// + /// The server is allowed to close connections which are idle for a certain amount of time. We + /// don't want to treat this as an error in the nasty client, as it is expected, and we should + /// simply reopen the stream. + /// /// If there is an error polling a WebSockets connection last used more recently than this /// duration, it is considered an error. If the connection is staler than this, it is only a /// warning, and the connection is automatically refreshed. @@ -143,7 +161,7 @@ struct ClientConfig { #[derive(Clone, Debug, Parser)] struct ActionDistribution { /// The weight of query actions in the random distribution. - #[clap(long, env = "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY", default_value = "5")] + #[clap(long, env = "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY", default_value = "20")] weight_query: u8, /// The weight of "open stream" actions in the random distribution. @@ -166,7 +184,7 @@ struct ActionDistribution { #[clap( long, env = "ESPRESSO_NASTY_CLIENT_WEIGHT_POLL_STREAM", - default_value = "5" + default_value = "10" )] weight_poll_stream: u8, @@ -174,7 +192,7 @@ struct ActionDistribution { #[clap( long, env = "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_WINDOW", - default_value = "3" + default_value = "15" )] weight_query_window: u8, @@ -182,9 +200,25 @@ struct ActionDistribution { #[clap( long, env = "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_NAMESPACE", - default_value = "3" + default_value = "15" )] weight_query_namespace: u8, + + /// The weight of "query block state" actions in the random distribution. + #[clap( + long, + env = "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_BLOCK_STATE", + default_value = "15" + )] + weight_query_block_state: u8, + + /// The weight of "query fee state" actions in the random distribution. + #[clap( + long, + env = "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_FEE_STATE", + default_value = "15" + )] + weight_query_fee_state: u8, } impl ActionDistribution { @@ -196,6 +230,8 @@ impl ActionDistribution { ActionDiscriminants::PollStream => self.weight_poll_stream, ActionDiscriminants::QueryWindow => self.weight_query_window, ActionDiscriminants::QueryNamespace => self.weight_query_namespace, + ActionDiscriminants::QueryBlockState => self.weight_query_block_state, + ActionDiscriminants::QueryFeeState => self.weight_query_fee_state, } } } @@ -209,6 +245,10 @@ struct Metrics { poll_stream_actions: HashMap>, query_window_actions: Box, query_namespace_actions: Box, + query_block_state_actions: Box, + query_fee_state_actions: Box, + slow_requests: Box, + request_latency: Box, } impl Metrics { @@ -275,6 +315,16 @@ impl Metrics { query_window_actions: registry.create_counter("query_window_actions".into(), None), query_namespace_actions: registry .create_counter("query_namespace_actions".into(), None), + query_block_state_actions: registry + .create_counter("query_block_state_actions".into(), None), + query_fee_state_actions: registry + .create_counter("query_fee_state_actions".into(), None), + slow_requests: registry + .subgroup("http".into()) + .create_counter("slow_requests".into(), None), + request_latency: registry + .subgroup("http".into()) + .create_histogram("latency".into(), Some("s".into())), } } } @@ -285,43 +335,69 @@ trait Queryable: DeserializeOwned + Debug + Eq { /// URL segment used to indicate that we want to fetch this resource by block hash. const HASH_URL_SEGMENT: &'static str; + /// URL segment used to indicate that we want to fetch this resource by payload hash. + /// + /// This may be none if the resource does not support fetching by payload hash. + const PAYLOAD_HASH_URL_SEGMENT: Option<&'static str>; + fn hash(&self) -> String; + fn payload_hash(&self) -> String; } impl Queryable for BlockQueryData { const RESOURCE: Resource = Resource::Blocks; const HASH_URL_SEGMENT: &'static str = "hash"; + const PAYLOAD_HASH_URL_SEGMENT: Option<&'static str> = Some("payload-hash"); fn hash(&self) -> String { self.hash().to_string() } + + fn payload_hash(&self) -> String { + self.payload_hash().to_string() + } } impl Queryable for LeafQueryData { const RESOURCE: Resource = Resource::Leaves; const HASH_URL_SEGMENT: &'static str = "hash"; + const PAYLOAD_HASH_URL_SEGMENT: Option<&'static str> = None; fn hash(&self) -> String { self.hash().to_string() } + + fn payload_hash(&self) -> String { + self.payload_hash().to_string() + } } impl Queryable for Header { const RESOURCE: Resource = Resource::Headers; const HASH_URL_SEGMENT: &'static str = "hash"; + const PAYLOAD_HASH_URL_SEGMENT: Option<&'static str> = Some("payload-hash"); fn hash(&self) -> String { self.commit().to_string() } + + fn payload_hash(&self) -> String { + self.payload_commitment.to_string() + } } impl Queryable for PayloadQueryData { const RESOURCE: Resource = Resource::Payloads; const HASH_URL_SEGMENT: &'static str = "block-hash"; + const PAYLOAD_HASH_URL_SEGMENT: Option<&'static str> = Some("hash"); fn hash(&self) -> String { self.block_hash().to_string() } + + fn payload_hash(&self) -> String { + self.hash().to_string() + } } type Connection = socket::Connection; @@ -348,7 +424,7 @@ impl ResourceManager { fn new(opt: &Options, metrics: Arc) -> Self { Self { client: surf_disco::Client::builder(opt.url.clone()) - .set_timeout(Some(opt.http_timeout)) + .set_timeout(Some(opt.client_config.http_timeout_error)) .build(), open_streams: BTreeMap::new(), next_stream_id: 0, @@ -417,17 +493,43 @@ impl ResourceManager { } } + /// Send an HTTP GET request and deserialize the response. + /// + /// This method is a wrapper around `self.client.get()`, which adds instrumentation and metrics + /// for request latency. + async fn get(&self, path: impl Into) -> anyhow::Result { + let path = path.into(); + tracing::debug!("-> GET {path}"); + + let start = Instant::now(); + let res = self.client.get::(&path).send().await; + let elapsed = start.elapsed(); + + let status = match &res { + Ok(_) => StatusCode::OK, + Err(err) => err.status(), + }; + tracing::debug!("<- GET {path} {} ({elapsed:?})", u16::from(status)); + + self.metrics + .request_latency + .add_point((elapsed.as_millis() as f64) / 1000.); + if elapsed >= self.cfg.http_timeout_warning { + self.metrics.slow_requests.add(1); + tracing::warn!(%path, ?elapsed, "slow request"); + } + + res.context(format!("GET {path}")) + } + async fn query(&self, at: u64) -> anyhow::Result<()> { let at = self.adjust_index(at).await?; let obj = self .retry( info_span!("query", resource = Self::singular(), at), || async { - self.client - .get::(&format!("availability/{}/{at}", Self::singular())) - .send() + self.get::(format!("availability/{}/{at}", Self::singular())) .await - .context(format!("fetching {} {at}", Self::singular())) }, ) .await?; @@ -438,15 +540,12 @@ impl ResourceManager { .retry( info_span!("query by hash", resource = Self::singular(), at, hash), || async { - self.client - .get(&format!( - "availability/{}/{}/{hash}", - Self::singular(), - T::HASH_URL_SEGMENT, - )) - .send() - .await - .context(format!("fetching {} {hash}", Self::singular())) + self.get(format!( + "availability/{}/{}/{hash}", + Self::singular(), + T::HASH_URL_SEGMENT, + )) + .await }, ) .await?; @@ -458,6 +557,37 @@ impl ResourceManager { ) ); + // Query by payload hash and check consistency. + if let Some(segment) = T::PAYLOAD_HASH_URL_SEGMENT { + let payload_hash = obj.payload_hash(); + let by_payload_hash = self + .retry( + info_span!( + "query by payload hash", + resource = Self::singular(), + at, + payload_hash + ), + || async { + self.get::(format!( + "availability/{}/{segment}/{payload_hash}", + Self::singular(), + )) + .await + }, + ) + .await?; + // We might not get the exact object this time, due to non-uniqueness of payloads, but we + // should get an object with the same payload. + ensure!( + payload_hash == by_payload_hash.payload_hash(), + format!( + "query for {} {at} by payload hash {payload_hash} is not consistent", + Self::singular() + ) + ); + } + self.metrics.query_actions[&T::RESOURCE].add(1); Ok(()) } @@ -524,7 +654,8 @@ impl ResourceManager { let (id, stream) = self.open_streams.iter_mut().nth(index).unwrap(); // Check if the next item is immediately available or if we're going to block. - if stream.stream.as_mut().peek().now_or_never().is_none() { + let will_block = stream.stream.as_mut().peek().now_or_never().is_none(); + if will_block { blocking += 1; if blocking > self.cfg.max_blocking_polls { tracing::info!("aborting poll_stream action; exceeded maximum blocking polls"); @@ -554,10 +685,20 @@ impl ResourceManager { }; match res { Ok(obj) => { - // Successfully polling a WebSockets connection should reset the connection - // timeout, so we don't expect errors from this connection in the near - // future. - stream.refreshed = Instant::now(); + if will_block { + // Successfully reading from a WebSockets stream should reset the idle + // conenection timeout, so we don't expect errors from this connection + // in the near future. Note that this applies only to reads which + // actually block. Reads which don't block may come directly from the + // local TCP buffer, and thus not generate any traffic on the idle TCP + // connection. + stream.refreshed = Instant::now(); + tracing::debug!( + refreshed = ?stream.refreshed, + "{} stream refreshed due to blocking read", + Self::singular(), + ); + } break obj; } Err(err) if refreshed.elapsed() >= self.cfg.web_socket_timeout => { @@ -572,6 +713,11 @@ impl ResourceManager { .context(format!("subscribing to {} from {pos}", Self::plural()))?; stream.stream = Box::pin(conn.peekable()); stream.refreshed = Instant::now(); + tracing::info!( + refreshed = ?stream.refreshed, + "{} stream refreshed due to connection reset", + Self::singular(), + ); } Err(err) => { // Errors on a relatively fresh connection are not allowed. Close the stream @@ -592,11 +738,8 @@ impl ResourceManager { let id = *id; let expected = self .retry(info_span!("fetching expected object"), || async { - self.client - .get(&format!("availability/{}/{pos}", Self::singular())) - .send() + self.get(format!("availability/{}/{pos}", Self::singular())) .await - .context(format!("fetching {} {pos}", Self::singular())) }) .await?; ensure!( @@ -613,12 +756,7 @@ impl ResourceManager { async fn adjust_index(&self, at: u64) -> anyhow::Result { let block_height = loop { - let block_height: u64 = self - .client - .get("status/block-height") - .send() - .await - .context("getting block height")?; + let block_height: u64 = self.get("status/block-height").await?; if block_height == 0 { // None of our tests work with an empty history, but if we just wait briefly there // should be some blocks produced soon. @@ -642,13 +780,10 @@ impl ResourceManager
{ .retry( info_span!("timestamp window", resource = Self::singular(), start, end), || async { - self.client - .get::>(&format!( - "node/header/window/{start}/{end}" - )) - .send() - .await - .context(format!("fetching timestamp window from {start} to {end}")) + self.get::>(format!( + "node/header/window/{start}/{end}" + )) + .await }, ) .await?; @@ -701,6 +836,166 @@ impl ResourceManager
{ self.metrics.query_window_actions.add(1); Ok(()) } + + async fn query_block_state(&self, block: u64, index: u64) -> anyhow::Result<()> { + let (block, index) = match self.adjust_index(block).await? { + 0 | 1 => { + // The block state at height 0 is empty, so to have a valid query just adjust to + // querying at height 1. At height 1, the only valid index to query is 0. + (1, 0) + } + block => { + // At any other height, all indices between 0 and `block - 1` are valid to query. + (block, index % (block - 1)) + } + }; + + // Get the header of the state snapshot we're going to query and the block commitment we're + // going to look up from the Merkle tree, so we can later verify our results. + let block_header = self + .retry(info_span!("get block header", block), || async { + self.get::
(format!("availability/header/{block}")) + .await + }) + .await?; + let index_header = self + .retry(info_span!("get index header", index), || async { + self.get::
(format!("availability/header/{index}")) + .await + }) + .await?; + + // Get a Merkle proof for the block commitment at position `index` from state `block`. + let proof = self + .retry(info_span!("get block proof", block, index), || async { + self.get::<::MembershipProof>(format!( + "block-state/{block}/{index}" + )) + .await + }) + .await?; + + // Check that the proof proves inclusion of `index_header` at position `index` relative to + // `block_header`. + BlockMerkleTree::verify(block_header.block_merkle_tree_root.digest(), index, &proof) + .context("malformed merkle proof")? + .or_else(|_| bail!("invalid merkle proof"))?; + ensure!( + proof.elem() == Some(&index_header.commit()), + "merkle proof is for wrong element: {:?} != {:?}", + proof.elem(), + index_header.commit() + ); + + // Look up the proof a different way, by state commitment, and check that we get the same + // proof. + let proof2 = self + .retry( + info_span!( + "get block proof by state commitment", + block, + index, + commitment = %block_header.block_merkle_tree_root, + ), + || async { + self.get::<::MembershipProof>(format!( + "block-state/commit/{}/{index}", + block_header.block_merkle_tree_root, + )) + .await + }, + ) + .await?; + ensure!( + proof2 == proof, + "got a different proof when querying by commitment, {proof2:?} != {proof:?}" + ); + + self.metrics.query_block_state_actions.add(1); + Ok(()) + } + + async fn query_fee_state(&self, block: u64, builder: u64) -> anyhow::Result<()> { + let block = self.adjust_index(block).await?; + let builder = if block == 0 { 0 } else { builder % block }; + + // Get the header of block `builder` so we can get an address (the builder account) to + // query. + let builder_header = self + .retry(info_span!("get builder header", builder), || async { + self.get::
(format!("availability/header/{builder}")) + .await + }) + .await?; + let builder_address = builder_header.fee_info.account(); + + // Get the header of the state snapshot we're going to query so we can later verify our + // results. + let block_header = self + .retry(info_span!("get block header", block), || async { + self.get::
(format!("availability/header/{block}")) + .await + }) + .await?; + + // Get a Merkle proof for the fee state of `builder_address` from state `block`. + let proof = self + .retry( + info_span!("get account proof", block, %builder_address), + || async { + self.get::<::MembershipProof>(&format!( + "fee-state/{block}/{builder_address}" + )) + .await + }, + ) + .await?; + + // Check that the proof is valid relative to `builder_header`. + if proof.elem().is_some() { + FeeMerkleTree::verify( + block_header.fee_merkle_tree_root.digest(), + builder_address, + &proof, + ) + .context("malformed membership proof")? + .or_else(|_| bail!("invalid membership proof"))?; + } else { + ensure!( + FeeMerkleTree::from_commitment(block_header.fee_merkle_tree_root) + .non_membership_verify(builder_address, &proof) + .context("malformed non-membership proof")?, + "invalid non-membership proof" + ); + } + + // Look up the proof a different way, by state commitment, and check that we get the same + // proof. + let proof2 = self + .retry( + info_span!( + "get account proof by state commitment", + block, + %builder_address, + commitment = %block_header.fee_merkle_tree_root, + ), + || async { + self.get::<::MembershipProof>(format!( + "fee-state/commit/{}/{builder_address}", + block_header.fee_merkle_tree_root, + )) + .await + }, + ) + .await?; + ensure!( + proof2 == proof, + "got a different proof when querying by commitment, {proof2:?} != {proof:?}" + ); + + self.metrics.query_fee_state_actions.add(1); + Ok(()) + } } impl ResourceManager> { @@ -713,46 +1008,45 @@ impl ResourceManager> { // the namespace table. let header: Header = self .retry(info_span!("fetch header"), || async { - self.client - .get(&format!("availability/header/{block}")) - .send() - .await - .context(format!("fetching header {block}")) + self.get(format!("availability/header/{block}")).await }) .await?; - if header.ns_table.is_empty() { + let num_namespaces = header.ns_table.iter().count(); + if num_namespaces == 0 { tracing::info!("not fetching namespace because block {block} is empty"); return Ok(()); } - let ns = header.ns_table.get_table_entry(index).0; + let ns_index = header.ns_table.iter().nth(index % num_namespaces).unwrap(); + let ns = header.ns_table.read_ns_id(&ns_index).unwrap(); let ns_proof: NamespaceProofQueryData = self .retry(info_span!("fetch namespace", %ns), || async { - self.client - .get(&format!("availability/block/{block}/namespace/{ns}")) - .send() + self.get(format!("availability/block/{block}/namespace/{ns}")) .await - .context(format!("fetching namespace {block}:{ns}")) }) .await?; // Verify proof. let vid_common: VidCommonQueryData = self .retry(info_span!("fetch VID common"), || async { - self.client - .get(&format!("availability/vid/common/{block}")) - .send() - .await - .context(format!("fetching VID common {block}")) + self.get(format!("availability/vid/common/{block}")).await }) .await?; - let vid = vid_scheme(VidSchemeType::get_num_storage_nodes(vid_common.common()) as usize); + ensure!( + ns_proof.proof.is_some(), + format!("missing namespace proof for {block}:{ns}") + ); ensure!( ns_proof .proof - .verify(&vid, &header.payload_commitment, &header.ns_table) + .unwrap() + .verify( + &header.ns_table, + &header.payload_commitment, + vid_common.common() + ) .is_some(), - format!("namespace proof for {block}:{ns} is invalid") + format!("failure to verify namespace proof for {block}:{ns}") ); self.metrics.query_namespace_actions.add(1); @@ -820,6 +1114,16 @@ enum Action { block: u64, namespace: usize, }, + QueryBlockState { + block: u64, + index: u64, + }, + QueryFeeState { + block: u64, + // The index of the block whose builder address should be looked up. This leads to more + // realistic queries than just randomly generating addresses. + builder: u64, + }, } impl Action { @@ -853,6 +1157,14 @@ impl Action { block: rng.next_u64(), namespace: rng.next_u32() as usize, }, + ActionDiscriminants::QueryBlockState => Self::QueryBlockState { + block: rng.next_u64(), + index: rng.next_u64(), + }, + ActionDiscriminants::QueryFeeState => Self::QueryFeeState { + block: rng.next_u64(), + builder: rng.next_u64(), + }, } } } @@ -917,6 +1229,12 @@ impl Client { Action::QueryNamespace { block, namespace } => { self.blocks.query_namespace(block, namespace).await } + Action::QueryBlockState { block, index } => { + self.headers.query_block_state(block, index).await + } + Action::QueryFeeState { block, builder } => { + self.headers.query_fee_state(block, builder).await + } } } } @@ -951,6 +1269,10 @@ async fn main() { let metrics = PrometheusMetrics::default(); let total_actions = metrics.create_counter("total_actions".into(), None); let failed_actions = metrics.create_counter("failed_actions".into(), None); + metrics + .subgroup("http".into()) + .create_gauge("slow_request_threshold".into(), Some("s".into())) + .set(opt.client_config.http_timeout_warning.as_secs() as usize); let mut client = Client::new(&opt, &metrics); let mut rng = rand::thread_rng(); diff --git a/sequencer/src/bin/orchestrator.rs b/sequencer/src/bin/orchestrator.rs index 8258f04e8..143645f65 100644 --- a/sequencer/src/bin/orchestrator.rs +++ b/sequencer/src/bin/orchestrator.rs @@ -4,13 +4,15 @@ use derive_more::From; use ethers::utils::hex::{self, FromHexError}; use hotshot_orchestrator::config::Libp2pConfig; use hotshot_orchestrator::{config::NetworkConfig, run_orchestrator}; -use sequencer::{options::parse_duration, PubKey}; +use sequencer::{ + options::{parse_duration, Ratio}, + PubKey, +}; use snafu::Snafu; -use std::fmt::{self, Display, Formatter}; -use std::num::{NonZeroUsize, ParseIntError}; -use std::str::FromStr; +use std::num::NonZeroUsize; use std::time::Duration; use url::Url; +use vec1::Vec1; #[derive(Parser)] struct Args { @@ -18,6 +20,10 @@ struct Args { #[clap(short, long, env = "ESPRESSO_ORCHESTRATOR_PORT")] port: u16, + /// Port to run the server on. + #[clap(short, long, env = "ESPRESSO_ORCHESTRATOR_MANUAL_START_PASSWORD")] + manual_start_password: Option, + /// Number of nodes in the network. #[clap(short, long, env = "ESPRESSO_ORCHESTRATOR_NUM_NODES")] num_nodes: NonZeroUsize, @@ -43,6 +49,14 @@ struct Args { )] timeout_ratio: Ratio, + /// The threshold + #[arg( + long, + env = "ESPRESSO_ORCHESTRATOR_START_THRESHOLD", + default_value = "8:10" + )] + start_threshold: Ratio, + /// The delay a leader inserts before starting pre-commit. #[arg( long, @@ -64,8 +78,8 @@ struct Args { keygen_seed: [u8; 32], /// HotShot builder URL - #[arg(long, env = "ESPRESSO_ORCHESTRATOR_BUILDER_URL")] - builder_url: Url, + #[arg(long, env = "ESPRESSO_ORCHESTRATOR_BUILDER_URLS", num_args = 1.., value_delimiter = ',')] + builder_urls: Vec, /// The maximum amount of time a leader can wait to get a block from a builder. /// @@ -94,52 +108,6 @@ fn parse_seed(s: &str) -> Result<[u8; 32], ParseSeedError> { .map_err(|vec| ParseSeedError::WrongLength { length: vec.len() }) } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Ratio { - numerator: u64, - denominator: u64, -} - -impl From for (u64, u64) { - fn from(r: Ratio) -> Self { - (r.numerator, r.denominator) - } -} - -impl Display for Ratio { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}:{}", self.numerator, self.denominator) - } -} - -#[derive(Debug, Snafu)] -enum ParseRatioError { - #[snafu(display("numerator and denominator must be separated by :"))] - MissingDelimiter, - InvalidNumerator { - err: ParseIntError, - }, - InvalidDenominator { - err: ParseIntError, - }, -} - -impl FromStr for Ratio { - type Err = ParseRatioError; - - fn from_str(s: &str) -> Result { - let (num, den) = s.split_once(':').ok_or(ParseRatioError::MissingDelimiter)?; - Ok(Self { - numerator: num - .parse() - .map_err(|err| ParseRatioError::InvalidNumerator { err })?, - denominator: den - .parse() - .map_err(|err| ParseRatioError::InvalidDenominator { err })?, - }) - } -} - #[async_std::main] async fn main() { setup_logging(); @@ -147,6 +115,8 @@ async fn main() { let args = Args::parse(); let mut config = NetworkConfig:: { start_delay_seconds: args.start_delay.as_secs(), + manual_start_password: args.manual_start_password, + indexed_da: false, ..Default::default() }; @@ -177,11 +147,12 @@ async fn main() { config.config.next_view_timeout = args.next_view_timeout.as_millis() as u64; config.libp2p_config = Some(libp2p_config); config.config.timeout_ratio = args.timeout_ratio.into(); + config.config.start_threshold = args.start_threshold.into(); config.config.round_start_delay = args.round_start_delay.as_millis() as u64; config.config.start_delay = args.start_delay.as_millis() as u64; config.config.da_staked_committee_size = args.num_nodes.get(); config.config.da_non_staked_committee_size = 0; - config.config.builder_url = args.builder_url; + config.config.builder_urls = Vec1::try_from_vec(args.builder_urls).unwrap(); config.config.builder_timeout = args.builder_timeout; run_orchestrator( config, diff --git a/sequencer/src/bin/pub-key.rs b/sequencer/src/bin/pub-key.rs index f6adb03a2..16ab6584e 100644 --- a/sequencer/src/bin/pub-key.rs +++ b/sequencer/src/bin/pub-key.rs @@ -1,5 +1,6 @@ use anyhow::bail; use clap::Parser; +use hotshot::{traits::implementations::derive_libp2p_peer_id, types::BLSPubKey}; use hotshot_types::{ light_client::{StateKeyPair, StateSignKey}, traits::signature_key::SignatureKey, @@ -30,13 +31,33 @@ impl FromStr for PrivateKey { /// Get the public key corresponding to a private key. #[derive(Clone, Debug, Parser)] struct Options { + /// The private key to get the public key for. key: PrivateKey, + + // Whether or not to derive the libp2p peer ID from the private key. + #[clap(long, short)] + libp2p: bool, } fn main() { let opt = Options::parse(); - match opt.key { - PrivateKey::Bls(key) => println!("{}", PubKey::from_private(&key)), - PrivateKey::Schnorr(key) => println!("{}", StateKeyPair::from_sign_key(key).ver_key()), + + match (opt.libp2p, opt.key) { + // Non-libp2p + (false, PrivateKey::Bls(key)) => println!("{}", PubKey::from_private(&key)), + (false, PrivateKey::Schnorr(key)) => { + println!("{}", StateKeyPair::from_sign_key(key).ver_key()) + } + + // Libp2p + (true, PrivateKey::Bls(key)) => { + println!( + "{}", + derive_libp2p_peer_id::(&key).expect("Failed to derive libp2p peer ID") + ); + } + (true, _) => { + eprintln!("Key type unsupported for libp2p peer ID derivation"); + } } } diff --git a/sequencer/src/bin/reset-storage.rs b/sequencer/src/bin/reset-storage.rs index 06c275122..0195635ad 100644 --- a/sequencer/src/bin/reset-storage.rs +++ b/sequencer/src/bin/reset-storage.rs @@ -15,7 +15,7 @@ enum Options { /// Reset file system storage. Fs(persistence::fs::Options), /// Reset SQL storage. - Sql(persistence::sql::Options), + Sql(Box), } #[async_std::main] @@ -31,7 +31,7 @@ async fn main() -> anyhow::Result<()> { } Options::Sql(opt) => { tracing::warn!("resetting SQL storage {opt:?}"); - reset_storage(opt).await + reset_storage(*opt).await } } } diff --git a/sequencer/src/bin/state-relay-server.rs b/sequencer/src/bin/state-relay-server.rs index cb14dcc62..5b77ec490 100644 --- a/sequencer/src/bin/state-relay-server.rs +++ b/sequencer/src/bin/state-relay-server.rs @@ -1,6 +1,8 @@ use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; use clap::Parser; use es_version::SEQUENCER_VERSION; +use ethers::types::U256; +use hotshot_state_prover::service::one_honest_threshold; use sequencer::state_signature::relay_server::run_relay_server; #[derive(Parser)] @@ -14,16 +16,15 @@ struct Args { )] port: u16, - /// Threshold to form an available state signature package. + /// Total amount of stake. /// WARNING: this is a temporary flag, should remove after integrating with stake table. /// Related issue: [https://github.com/EspressoSystems/espresso-sequencer/issues/1022] #[clap( - short, long, - env = "ESPRESSO_STATE_SIGNATURE_WEIGHT_THRESHOLD", - default_value = "3" + env = "ESPRESSO_STATE_SIGNATURE_TOTAL_STAKE", + default_value = "5" )] - threshold: u64, + total_stake: u64, } #[async_std::main] @@ -32,11 +33,15 @@ async fn main() { setup_backtrace(); let args = Args::parse(); + let threshold = one_honest_threshold(U256::from(args.total_stake)); - tracing::info!("starting state relay server on port {}", args.port); + tracing::info!( + port = args.port, + "starting state relay server, quorum threshold: {threshold}" + ); run_relay_server( None, - args.threshold, + threshold, format!("http://0.0.0.0:{}", args.port).parse().unwrap(), SEQUENCER_VERSION, ) diff --git a/sequencer/src/bin/submit-transactions.rs b/sequencer/src/bin/submit-transactions.rs index 8c76d457f..ba2941d04 100644 --- a/sequencer/src/bin/submit-transactions.rs +++ b/sequencer/src/bin/submit-transactions.rs @@ -41,13 +41,26 @@ struct Options { #[clap(long, name = "MAX_SIZE", default_value = "1kb", value_parser = parse_size, env = "ESPRESSO_SUBMIT_TRANSACTIONS_MAX_SIZE")] max_size: u64, + /// Minimum size of batch of transactions to submit. + /// + /// Batches will be a random count between MIN_BATCH_SIZE and MAX_BATCH_SIZE, with a falling distribution favoring smaller batches. + /// This is by selecting a random size S on each iteration I since last batch, and collecting a batch whenever that S <= I. + #[clap(long, name = "MIN_BATCH_SIZE", default_value = "1", value_parser = parse_size, env = "ESPRESSO_SUBMIT_TRANSACTIONS_MIN_BATCH_SIZE")] + min_batch_size: u64, + + /// Maximum size of batch of transactions to submit. + /// + /// Batches will be a random count between MIN_BATCH_SIZE and MAX_BATCH_SIZE, with a falling distribution favoring smaller batches. + #[clap(long, name = "MAX_BATCH_SIZE", default_value = "20", value_parser = parse_size, env = "ESPRESSO_SUBMIT_TRANSACTIONS_MAX_BATCH_SIZE")] + max_batch_size: u64, + /// Minimum namespace ID to submit to. #[clap( long, default_value = "10000", env = "ESPRESSO_SUBMIT_TRANSACTIONS_MIN_NAMESPACE" )] - min_namespace: u64, + min_namespace: u32, /// Maximum namespace ID to submit to. #[clap( @@ -55,7 +68,7 @@ struct Options { default_value = "10010", env = "ESPRESSO_SUBMIT_TRANSACTIONS_MAX_NAMESPACE" )] - max_namespace: u64, + max_namespace: u32, /// Mean delay between submitting transactions. /// @@ -119,6 +132,9 @@ impl Options { .clone() .unwrap_or_else(|| self.url.join("submit").unwrap()) } + fn use_public_mempool(&self) -> bool { + self.submit_url.is_none() + } } #[async_std::main] @@ -241,32 +257,63 @@ async fn submit_transactions( // mean `opt.delay`, or parameter `\lambda = 1 / opt.delay`. let delay_distr = rand_distr::Exp::::new(1f64 / opt.delay.as_millis() as f64).unwrap(); + let mut txns = Vec::new(); + let mut hashes = Vec::new(); loop { let tx = random_transaction(&opt, &mut rng); let hash = tx.commit(); tracing::info!( - "submitting transaction {hash} for namespace {} of size {}", + "Adding transaction {hash} for namespace {} of size {}", tx.namespace(), tx.payload().len() ); - if let Err(err) = client - .post::<()>("submit") - .body_binary(&tx) - .unwrap() - .send() - .await - { - tracing::error!("failed to submit transaction: {err}"); + txns.push(tx); + hashes.push(hash); + + let randomized_batch_size = if opt.use_public_mempool() { + 1 + } else { + rng.gen_range(opt.min_batch_size..=opt.max_batch_size) + }; + let txns_batch_count = txns.len() as u64; + if randomized_batch_size <= txns_batch_count { + if let Err(err) = if txns_batch_count == 1 { + // occasionally test the 'submit' endpoint, just for coverage + client + .post::<()>("submit") + .body_binary(&txns[0]) + .unwrap() + .send() + .await + } else { + client + .post::<()>("batch") + .body_binary(&txns) + .unwrap() + .send() + .await + } { + tracing::error!("failed to submit batch of {txns_batch_count} transactions: {err}"); + } else { + tracing::info!("submitted batch of {txns_batch_count} transactions"); + let submitted_at = Instant::now(); + for hash in hashes.iter() { + sender + .send(SubmittedTransaction { + hash: *hash, + submitted_at, + }) + .await + .ok(); + } + } + txns.clear(); + hashes.clear(); + + let delay = Duration::from_millis(delay_distr.sample(&mut rng) as u64); + tracing::info!("sleeping for {delay:?}"); + sleep(delay).await; } - let submitted_at = Instant::now(); - sender - .send(SubmittedTransaction { hash, submitted_at }) - .await - .ok(); - - let delay = Duration::from_millis(delay_distr.sample(&mut rng) as u64); - tracing::info!("sleeping for {delay:?}"); - sleep(delay).await; } } @@ -280,6 +327,8 @@ async fn server(port: u16, bind_version: Ver) } fn random_transaction(opt: &Options, rng: &mut ChaChaRng) -> Transaction { + // TODO instead use NamespaceId::random, but that does not allow us to + // enforce `gen_range(opt.min_namespace..=opt.max_namespace)` let namespace = rng.gen_range(opt.min_namespace..=opt.max_namespace); let len = rng.gen_range(opt.min_size..=opt.max_size); diff --git a/sequencer/src/block.rs b/sequencer/src/block.rs index 2f1ab799b..033c7253f 100644 --- a/sequencer/src/block.rs +++ b/sequencer/src/block.rs @@ -1,203 +1,8 @@ -use std::sync::Arc; +mod full_payload; +mod namespace_payload; +mod uint_bytes; -use crate::{BlockBuildingSnafu, NodeState, Transaction}; -use committable::{Commitment, Committable}; -use hotshot_query_service::availability::QueryablePayload; -use hotshot_types::traits::{states::InstanceState, BlockPayload}; -use hotshot_types::utils::BuilderCommitment; -use serde::{Deserialize, Serialize}; -use sha2::Digest; -use snafu::OptionExt; - -pub mod entry; -pub mod payload; -pub mod queryable; -pub mod tables; -pub mod tx_iterator; - -use entry::TxTableEntryWord; -use payload::Payload; -use tables::NameSpaceTable; - -pub type NsTable = NameSpaceTable; - -impl BlockPayload for Payload { - type Error = crate::Error; - type Transaction = Transaction; - type Metadata = NsTable; - - /// Returns (Self, metadata). - /// - /// `metadata` is a bytes representation of the namespace table. - /// Why bytes? To make it easy to move metadata into payload in the future. - /// - /// Namespace table defined as follows for j>0: - /// word[0]: [number of entries in namespace table] - /// word[2j-1]: [id for the jth namespace] - /// word[2j]: [end byte index of the jth namespace in the payload] - /// - /// Thus, for j>2 the jth namespace payload bytes range is word[2(j-1)]..word[2j]. - /// Edge case: for j=1 the jth namespace start index is implicitly 0. - /// - /// Word type is `TxTableEntry`. - /// TODO(746) don't use `TxTableEntry`; make a different type for type safety. - /// - /// TODO final entry should be implicit: - /// https://github.com/EspressoSystems/espresso-sequencer/issues/757 - /// - /// TODO(746) refactor and make pretty "table" code for tx, namespace tables? - fn from_transactions( - txs: impl IntoIterator, - _state: Arc, - ) -> Result<(Self, Self::Metadata), Self::Error> { - let payload = Payload::from_txs(txs)?; - let ns_table = payload.get_ns_table().clone(); // TODO don't clone ns_table - Some((payload, ns_table)).context(BlockBuildingSnafu) - } - - fn from_bytes(encoded_transactions: &[u8], metadata: &Self::Metadata) -> Self { - Self { - raw_payload: encoded_transactions.to_vec(), - ns_table: metadata.clone(), // TODO don't clone ns_table - } - } - - // TODO remove - fn genesis() -> (Self, Self::Metadata) { - // this is only called from `Leaf::genesis`. Since we are - // passing empty list, max_block_size is irrelevant so we can - // use the mock NodeState. A future update to HotShot should - // make a change there to remove the need for this workaround. - - Self::from_transactions([], Arc::new(NodeState::mock())).unwrap() - } - - fn encode(&self) -> Result, Self::Error> { - Ok(Arc::from(self.raw_payload.clone())) - } - - fn transaction_commitments(&self, meta: &Self::Metadata) -> Vec> { - self.enumerate(meta).map(|(_, tx)| tx.commit()).collect() - } - - /// Generate commitment that builders use to sign block options. - fn builder_commitment(&self, metadata: &Self::Metadata) -> BuilderCommitment { - let mut digest = sha2::Sha256::new(); - digest.update((self.raw_payload.len() as u64).to_le_bytes()); - digest.update((self.ns_table.bytes.len() as u64).to_le_bytes()); - digest.update((metadata.bytes.len() as u64).to_le_bytes()); - digest.update(&self.raw_payload); - digest.update(&self.ns_table.bytes); - digest.update(&metadata.bytes); - BuilderCommitment::from_raw_digest(digest.finalize()) - } - - fn get_transactions<'a>( - &'a self, - metadata: &'a Self::Metadata, - ) -> impl 'a + Iterator { - self.enumerate(metadata).map(|(_, t)| t) - } -} +pub use full_payload::{NsProof, NsTable, NsTableValidationError, Payload, PayloadByteLen}; #[cfg(test)] -mod reference { - //! Reference data types. - //! - //! This module provides some reference instantiations of various data types which have an - //! external, language-independent interface (e.g. commitment scheme). Ports of the sequencer to - //! other languages, as well as downstream packages written in other languages, can use these - //! references objects and their known commitments to check that their implementations of the - //! commitment scheme are compatible with this reference implementation. To get the byte - //! representation or U256 representation of a commitment for testing in other packages, run the - //! tests and look for "commitment bytes" or "commitment U256" in the logs. - //! - //! For convenience, the reference objects are provided in serialized form, as they will appear - //! in query service responses and the like, in the JSON files in the `data` directory of the - //! repo for this crate. These JSON files are compiled into the crate binary and deserialized in - //! this module to generate tests for the serialization format and commitment scheme. - //! - //! These tests may fail if you make a breaking change to a commitment scheme, serialization, - //! etc. If this happens, be sure you _want_ to break the API, and, if so, simply replace the - //! relevant constant in this module with the "actual" value that can be found in the logs of - //! the failing test. - - use super::*; - use crate::{Header, L1BlockInfo}; - use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; - use lazy_static::lazy_static; - use sequencer_utils::commitment_to_u256; - use serde::de::DeserializeOwned; - use serde_json::Value; - - macro_rules! load_reference { - ($name:expr) => { - serde_json::from_str(include_str!(std::concat!("../../data/", $name, ".json"))).unwrap() - }; - } - - lazy_static! { - pub static ref NS_TABLE: Value = load_reference!("ns_table"); - pub static ref L1_BLOCK: Value = load_reference!("l1_block"); - pub static ref HEADER: Value = load_reference!("header"); - pub static ref TRANSACTION: Value = load_reference!("transaction"); - } - - fn reference_test( - reference: Value, - expected: &str, - commit: impl FnOnce(&T) -> Commitment, - ) { - setup_logging(); - setup_backtrace(); - - let reference: T = serde_json::from_value(reference).unwrap(); - let actual = commit(&reference); - - // Print information about the commitment that might be useful in generating tests for other - // languages. - let bytes: &[u8] = actual.as_ref(); - let u256 = commitment_to_u256(actual); - tracing::info!("actual commitment: {}", actual); - tracing::info!("commitment bytes: {:?}", bytes); - tracing::info!("commitment U256: {}", u256); - - assert_eq!(actual, expected.parse().unwrap()); - } - - #[test] - fn test_reference_ns_table() { - reference_test::, _>( - NS_TABLE.clone(), - "NSTABLE~GL-lEBAwNZDldxDpySRZQChNnmn9vNzdIAL8W9ENOuh_", - |ns_table| ns_table.commit(), - ); - } - - #[test] - fn test_reference_l1_block() { - reference_test::( - L1_BLOCK.clone(), - "L1BLOCK~4HpzluLK2Isz3RdPNvNrDAyQcWOF2c9JeLZzVNLmfpQ9", - |block| block.commit(), - ); - } - - #[test] - fn test_reference_header() { - reference_test::( - HEADER.clone(), - "BLOCK~00ISpu2jHbXD6z-BwMkwR4ijGdgUSoXLp_2jIStmqBrD", - |header| header.commit(), - ); - } - - #[test] - fn test_reference_transaction() { - reference_test::( - TRANSACTION.clone(), - "TX~77xOf9b3_RtGwqQ7_zOPeuJRS0iZwF7EJiV_NzOv4uJ3", - |tx| tx.commit(), - ); - } -} +mod test; diff --git a/sequencer/src/block/entry.rs b/sequencer/src/block/entry.rs deleted file mode 100644 index 0ba801d49..000000000 --- a/sequencer/src/block/entry.rs +++ /dev/null @@ -1,85 +0,0 @@ -use super::{Deserialize, Serialize}; -use crate::NamespaceId; -use core::fmt; -use std::mem::size_of; - -// Use newtype pattern so that tx table entries cannot be confused with other types. -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Default)] -pub struct TxTableEntry(TxTableEntryWord); -// TODO Get rid of TxTableEntryWord. We might use const generics in order to parametrize the set of functions below with u32,u64 etc... -// See https://github.com/EspressoSystems/espresso-sequencer/issues/1076 -pub type TxTableEntryWord = u32; - -impl TxTableEntry { - pub const MAX: TxTableEntry = Self(TxTableEntryWord::MAX); - - /// Adds `rhs` to `self` in place. Returns `None` on overflow. - pub fn checked_add_mut(&mut self, rhs: Self) -> Option<()> { - self.0 = self.0.checked_add(rhs.0)?; - Some(()) - } - pub const fn zero() -> Self { - Self(0) - } - pub const fn one() -> Self { - Self(1) - } - pub const fn to_bytes(&self) -> [u8; size_of::()] { - self.0.to_le_bytes() - } - pub fn from_bytes(bytes: &[u8]) -> Option { - Some(Self(TxTableEntryWord::from_le_bytes( - bytes.try_into().ok()?, - ))) - } - /// Infallible constructor. - pub fn from_bytes_array(bytes: [u8; TxTableEntry::byte_len()]) -> Self { - Self(TxTableEntryWord::from_le_bytes(bytes)) - } - pub const fn byte_len() -> usize { - size_of::() - } - - pub fn from_usize(val: usize) -> Self { - Self( - val.try_into() - .expect("usize -> TxTableEntry should succeed"), - ) - } -} - -impl fmt::Display for TxTableEntry { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl TryFrom for TxTableEntry { - type Error = >::Error; - - fn try_from(value: usize) -> Result { - TxTableEntryWord::try_from(value).map(Self) - } -} -impl TryFrom for usize { - type Error = >::Error; - - fn try_from(value: TxTableEntry) -> Result { - usize::try_from(value.0) - } -} - -impl TryFrom for TxTableEntry { - type Error = >::Error; - - fn try_from(value: NamespaceId) -> Result { - TxTableEntryWord::try_from(u64::from(value)).map(Self) - } -} -impl TryFrom for NamespaceId { - type Error = >::Error; - - fn try_from(value: TxTableEntry) -> Result { - Ok((value.0 as u64).into()) - } -} diff --git a/sequencer/src/block/full_payload.rs b/sequencer/src/block/full_payload.rs new file mode 100644 index 000000000..61247ec87 --- /dev/null +++ b/sequencer/src/block/full_payload.rs @@ -0,0 +1,9 @@ +mod ns_proof; +mod ns_table; +mod payload; + +pub use ns_proof::NsProof; +pub use ns_table::{NsIndex, NsTable, NsTableValidationError}; +pub use payload::{Payload, PayloadByteLen}; + +pub(in crate::block) use ns_table::NsIter; diff --git a/sequencer/src/block/full_payload/ns_proof.rs b/sequencer/src/block/full_payload/ns_proof.rs new file mode 100644 index 000000000..104ca45f4 --- /dev/null +++ b/sequencer/src/block/full_payload/ns_proof.rs @@ -0,0 +1,172 @@ +use crate::{ + block::{ + full_payload::{NsIndex, NsTable, Payload, PayloadByteLen}, + namespace_payload::NsPayloadOwned, + }, + NamespaceId, Transaction, +}; +use hotshot_types::{ + traits::EncodeBytes, + vid::{vid_scheme, LargeRangeProofType, VidCommitment, VidCommon, VidSchemeType}, +}; +use jf_vid::{ + payload_prover::{PayloadProver, Statement}, + VidScheme, +}; +use serde::{Deserialize, Serialize}; + +/// Proof of correctness for namespace payload bytes in a block. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct NsProof { + ns_index: NsIndex, + ns_payload: NsPayloadOwned, + ns_proof: Option, // `None` if ns_payload is empty +} + +impl NsProof { + /// Returns the payload bytes for the `index`th namespace, along with a + /// proof of correctness for those bytes. Returns `None` on error. + /// + /// The namespace payload [`NsPayloadOwned`] is included as a hidden field + /// in the returned [`NsProof`]. A conventional API would instead return + /// `(NsPayload, NsProof)` and [`NsProof`] would not contain the namespace + /// payload. + /// ([`TxProof::new`](crate::block::namespace_payload::TxProof::new) + /// conforms to this convention.) In the future we should change this API to + /// conform to convention. But that would require a change to our RPC + /// endpoint API at [`endpoints`](crate::api::endpoints), which is a hassle. + pub fn new(payload: &Payload, index: &NsIndex, common: &VidCommon) -> Option { + let payload_byte_len = payload.byte_len(); + if !payload_byte_len.is_consistent(common) { + tracing::warn!( + "payload byte len {} inconsistent with common {}", + payload_byte_len, + VidSchemeType::get_payload_byte_len(common) + ); + return None; // error: payload byte len inconsistent with common + } + if !payload.ns_table().in_bounds(index) { + tracing::warn!("ns_index {:?} out of bounds", index); + return None; // error: index out of bounds + } + let ns_payload_range = payload.ns_table().ns_range(index, &payload_byte_len); + + // TODO vid_scheme() arg should be u32 to match get_num_storage_nodes + // https://github.com/EspressoSystems/HotShot/issues/3298 + let vid = vid_scheme( + VidSchemeType::get_num_storage_nodes(common) + .try_into() + .ok()?, // error: failure to convert u32 to usize + ); + + let ns_proof = if ns_payload_range.as_block_range().is_empty() { + None + } else { + Some( + vid.payload_proof(payload.encode(), ns_payload_range.as_block_range()) + .ok()?, // error: internal to payload_proof() + ) + }; + + Some(NsProof { + ns_index: index.clone(), + ns_payload: payload.read_ns_payload(&ns_payload_range).to_owned(), + ns_proof, + }) + } + + /// Verify a [`NsProof`] against a payload commitment. Returns `None` on + /// error or if verification fails. + /// + /// There is no [`NsPayload`](crate::block::namespace_payload::NsPayload) + /// arg because this data is already included in the [`NsProof`]. See + /// [`NsProof::new`] for discussion. + /// + /// If verification is successful then return `(Vec, + /// NamespaceId)` obtained by post-processing the underlying + /// [`NsPayload`](crate::block::namespace_payload::NsPayload). Why? This + /// method might be run by a client in a WASM environment who might be + /// running non-Rust code, in which case the client is unable to perform + /// this post-processing himself. + pub fn verify( + &self, + ns_table: &NsTable, + commit: &VidCommitment, + common: &VidCommon, + ) -> Option<(Vec, NamespaceId)> { + VidSchemeType::is_consistent(commit, common).ok()?; + if !ns_table.in_bounds(&self.ns_index) { + return None; // error: index out of bounds + } + + let range = ns_table + .ns_range(&self.ns_index, &PayloadByteLen::from_vid_common(common)) + .as_block_range(); + + match (&self.ns_proof, range.is_empty()) { + (Some(proof), false) => { + // TODO vid_scheme() arg should be u32 to match get_num_storage_nodes + // https://github.com/EspressoSystems/HotShot/issues/3298 + let vid = vid_scheme( + VidSchemeType::get_num_storage_nodes(common) + .try_into() + .ok()?, // error: failure to convert u32 to usize + ); + + vid.payload_verify( + Statement { + payload_subslice: self.ns_payload.as_bytes_slice(), + range, + commit, + common, + }, + proof, + ) + .ok()? // error: internal to payload_verify() + .ok()?; // verification failure + } + (None, true) => {} // 0-length namespace, nothing to verify + (None, false) => { + tracing::error!( + "ns verify: missing proof for nonempty ns payload range {:?}", + range + ); + return None; + } + (Some(_), true) => { + tracing::error!("ns verify: unexpected proof for empty ns payload range"); + return None; + } + } + + // verification succeeded, return some data + let ns_id = ns_table.read_ns_id_unchecked(&self.ns_index); + Some((self.ns_payload.export_all_txs(&ns_id), ns_id)) + } + + /// Return all transactions in the namespace whose payload is proven by + /// `self`. The namespace ID for each returned [`Transaction`] is set to + /// `ns_id`. + /// + /// # Design warning + /// + /// This method relies on a promise that a [`NsProof`] stores the entire + /// namespace payload. If in the future we wish to remove the payload from a + /// [`NsProof`] then this method can no longer be supported. + /// + /// In that case, use the following a workaround: + /// - Given a [`NamespaceId`], get a [`NsIndex`] `i` via + /// [`NsTable::find_ns_id`]. + /// - Use `i` to get a + /// [`NsPayload`](crate::block::namespace_payload::NsPayload) `p` via + /// [`Payload::ns_payload`]. + /// - Use `p` to get the desired [`Vec`] via + /// [`NsPayload::export_all_txs`](crate::block::namespace_payload::NsPayload::export_all_txs). + /// + /// This workaround duplicates the work done in [`NsProof::new`]. If you + /// don't like that then you could instead hack [`NsProof::new`] to return a + /// pair `(NsProof, Vec)`. + pub fn export_all_txs(&self, ns_id: &NamespaceId) -> Vec { + self.ns_payload.export_all_txs(ns_id) + } +} diff --git a/sequencer/src/block/full_payload/ns_table.rs b/sequencer/src/block/full_payload/ns_table.rs new file mode 100644 index 000000000..50ce3489e --- /dev/null +++ b/sequencer/src/block/full_payload/ns_table.rs @@ -0,0 +1,467 @@ +//! Types related to a namespace table. +//! +//! All code that needs to know the binary format of a namespace table is +//! restricted to this file. +//! +//! See [`NsTable`] for a full specification of the binary format of a namespace +//! table. +use crate::{ + block::{ + full_payload::payload::PayloadByteLen, + namespace_payload::NsPayloadRange, + uint_bytes::{ + bytes_serde_impl, u32_from_bytes, u32_to_bytes, usize_from_bytes, usize_to_bytes, + }, + }, + NamespaceId, +}; +use committable::{Commitment, Committable, RawCommitmentBuilder}; +use derive_more::Display; +use hotshot_types::traits::EncodeBytes; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ops::Range, sync::Arc}; +use thiserror::Error; + +/// Byte lengths for the different items that could appear in a namespace table. +const NUM_NSS_BYTE_LEN: usize = 4; +const NS_OFFSET_BYTE_LEN: usize = 4; + +// TODO prefer [`NS_ID_BYTE_LEN`] set to `8` because [`NamespaceId`] is a `u64` +// but we need to maintain serialization compatibility. +// https://github.com/EspressoSystems/espresso-sequencer/issues/1574 +const NS_ID_BYTE_LEN: usize = 4; + +/// Raw binary data for a namespace table. +/// +/// Any sequence of bytes is a valid [`NsTable`]. +/// +/// # Binary format of a namespace table +/// +/// Byte lengths for the different items that could appear in a namespace table +/// are specified in local private constants [`NUM_NSS_BYTE_LEN`], +/// [`NS_OFFSET_BYTE_LEN`], [`NS_ID_BYTE_LEN`]. +/// +/// ## Number of entries in the namespace table +/// +/// The first [`NUM_NSS_BYTE_LEN`] bytes of the namespace table indicate the +/// number `n` of entries in the table as a little-endian unsigned integer. If +/// the entire table length is smaller than [`NUM_NSS_BYTE_LEN`] then the +/// missing bytes are zero-padded. +/// +/// The bytes in the namespace table beyond the first [`NUM_NSS_BYTE_LEN`] bytes +/// encode table entries. Each entry consumes exactly [`NS_ID_BYTE_LEN`] `+` +/// [`NS_OFFSET_BYTE_LEN`] bytes. +/// +/// The number `n` could be anything, including a number much larger than the +/// number of entries that could fit in the namespace table. As such, the actual +/// number of entries in the table is defined as the minimum of `n` and the +/// maximum number of whole entries that could fit in the table. +/// +/// See [`Self::in_bounds`] for clarification. +/// +/// ## Namespace table entry +/// +/// ### Namespace ID +/// +/// The first [`NS_ID_BYTE_LEN`] bytes of each table entry indicate the +/// [`NamespaceId`] for this namespace. Any table entry whose [`NamespaceId`] is +/// a duplicate of a previous entry is ignored. A correct count of the number of +/// *unique* (non-ignored) entries is given by `NsTable::iter().count()`. +/// +/// ### Namespace offset +/// +/// The next [`NS_OFFSET_BYTE_LEN`] bytes of each table entry indicate the +/// end-index of a namespace in the block payload bytes +/// [`Payload`](super::payload::Payload). This end-index is a little-endian +/// unsigned integer. +/// +/// # How to deduce a namespace's byte range +/// +/// In order to extract the payload bytes of a single namespace `N` from the +/// block payload one needs both the start- and end-indices for `N`. +/// +/// See [`Self::ns_range`] for clarification. What follows is a description of +/// what's implemented in [`Self::ns_range`]. +/// +/// If `N` occupies the `i`th entry in the namespace table for `i>0` then the +/// start-index for `N` is defined as the end-index of the `(i-1)`th entry in +/// the table. +/// +/// Even if the `(i-1)`the entry would otherwise be ignored (due to a duplicate +/// [`NamespaceId`] or any other reason), that entry's end-index still defines +/// the start-index of `N`. This rule guarantees that both start- and +/// end-indices for any namespace `N` can be read from a constant-size byte +/// range in the namespace table, and it eliminates the need to traverse an +/// unbounded number of previous entries of the namespace table looking for a +/// previous non-ignored entry. +/// +/// The start-index of the 0th entry in the table is implicitly defined to be +/// `0`. +/// +/// The start- and end-indices `(declared_start, declared_end)` declared in the +/// namespace table could be anything. As such, the actual start- and +/// end-indices `(start, end)` are defined so as to ensure that the byte range +/// is well-defined and in-bounds for the block payload: +/// ```ignore +/// end = min(declared_end, block_payload_byte_length) +/// start = min(declared_start, end) +/// ``` +/// +/// In a "honestly-prepared" namespace table the end-index of the final +/// namespace equals the byte length of the block payload. (Otherwise the block +/// payload might have bytes that are not included in any namespace.) +/// +/// It is possible that a namespace table could indicate two distinct namespaces +/// whose byte ranges overlap, though no "honestly-prepared" namespace table +/// would do this. +/// +/// TODO prefer [`NsTable`] to be a newtype like this +/// ```ignore +/// #[repr(transparent)] +/// #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +/// #[serde(transparent)] +/// pub struct NsTable(#[serde(with = "base64_bytes")] Vec); +/// ``` +/// but we need to maintain serialization compatibility. +/// +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +// Boilerplate: `#[serde(remote = "Self")]` needed to check invariants on +// deserialization. See +// https://github.com/serde-rs/serde/issues/1220#issuecomment-382589140 +#[serde(remote = "Self")] +pub struct NsTable { + #[serde(with = "base64_bytes")] + bytes: Vec, +} + +// Boilerplate: `#[serde(remote = "Self")]` allows invariant checking on +// deserialization via re-implementation of `Deserialize` in terms of default +// derivation. See +// https://github.com/serde-rs/serde/issues/1220#issuecomment-382589140 +impl<'de> Deserialize<'de> for NsTable { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let unchecked = NsTable::deserialize(deserializer)?; + unchecked + .validate_deserialization_invariants() + .map_err(de::Error::custom)?; + Ok(unchecked) + } +} + +// Boilerplate: use of `#[serde(remote = "Self")]` must include a trivial +// `Serialize` impl. See +// https://github.com/serde-rs/serde/issues/1220#issuecomment-382589140 +impl Serialize for NsTable { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + NsTable::serialize(self, serializer) + } +} + +impl NsTable { + /// Search the namespace table for the ns_index belonging to `ns_id`. + pub fn find_ns_id(&self, ns_id: &NamespaceId) -> Option { + self.iter() + .find(|index| self.read_ns_id_unchecked(index) == *ns_id) + } + + /// Number of entries in the namespace table. + /// + /// Defined as the maximum number of entries that could fit in the namespace + /// table, ignoring what's declared in the table header. + pub fn len(&self) -> NumNss { + NumNss( + self.bytes.len().saturating_sub(NUM_NSS_BYTE_LEN) + / NS_ID_BYTE_LEN.saturating_add(NS_OFFSET_BYTE_LEN), + ) + } + + /// Iterator over all unique namespaces in the namespace table. + pub fn iter(&self) -> impl Iterator + '_ { + NsIter::new(&self.len()) + } + + /// Read the namespace id from the `index`th entry from the namespace table. + /// Returns `None` if `index` is out of bounds. + /// + /// TODO I want to restrict visibility to `pub(crate)` or lower but this + /// method is currently used in `nasty-client`. + pub fn read_ns_id(&self, index: &NsIndex) -> Option { + if !self.in_bounds(index) { + None + } else { + Some(self.read_ns_id_unchecked(index)) + } + } + + /// Like [`Self::read_ns_id`] except `index` is not checked. Use [`Self::in_bounds`] as needed. + pub fn read_ns_id_unchecked(&self, index: &NsIndex) -> NamespaceId { + let start = index.0 * (NS_ID_BYTE_LEN + NS_OFFSET_BYTE_LEN) + NUM_NSS_BYTE_LEN; + + // TODO hack to deserialize `NamespaceId` from `NS_ID_BYTE_LEN` bytes + // https://github.com/EspressoSystems/espresso-sequencer/issues/1574 + NamespaceId::from(u32_from_bytes::( + &self.bytes[start..start + NS_ID_BYTE_LEN], + )) + } + + /// Does the `index`th entry exist in the namespace table? + pub fn in_bounds(&self, index: &NsIndex) -> bool { + self.len().in_bounds(index) + } + + /// Are the bytes of this [`NsTable`] uncorrupted? + /// + /// # Checks + /// 1. Byte length must hold a whole number of entries. + /// 2. All namespace IDs and offsets must increase monotonically. Offsets + /// must be nonzero. + /// 3. Header consistent with byte length. (Obsolete after + /// .) + /// 4. Final offset must equal `payload_byte_len`. (Obsolete after + /// .) + /// If the namespace table is empty then `payload_byte_len` must be 0. + pub fn validate( + &self, + payload_byte_len: &PayloadByteLen, + ) -> Result<(), NsTableValidationError> { + use NsTableValidationError::*; + + // conditions 1-3 + self.validate_deserialization_invariants()?; + + // condition 4 + let len = self.len().0; + if len > 0 { + let final_ns_index = NsIndex(len - 1); + let final_offset = self.read_ns_offset_unchecked(&final_ns_index); + if final_offset != payload_byte_len.as_usize() { + return Err(InvalidFinalOffset); + } + } else if payload_byte_len.as_usize() != 0 { + return Err(ExpectNonemptyNsTable); + } + + Ok(()) + } + + // CRATE-VISIBLE HELPERS START HERE + + /// Read subslice range for the `index`th namespace from the namespace + /// table. + pub(in crate::block) fn ns_range( + &self, + index: &NsIndex, + payload_byte_len: &PayloadByteLen, + ) -> NsPayloadRange { + let end = self + .read_ns_offset_unchecked(index) + .min(payload_byte_len.as_usize()); + let start = if index.0 == 0 { + 0 + } else { + self.read_ns_offset_unchecked(&NsIndex(index.0 - 1)) + } + .min(end); + NsPayloadRange::new(start, end) + } + + // PRIVATE HELPERS START HERE + + /// Read the number of namespaces declared in the namespace table. THIS + /// QUANTITY IS NEVER USED. Instead use [`NsTable::len`]. + /// + /// TODO Delete this method after + /// + fn read_num_nss(&self) -> usize { + let num_nss_byte_len = NUM_NSS_BYTE_LEN.min(self.bytes.len()); + usize_from_bytes::(&self.bytes[..num_nss_byte_len]) + } + + /// Read the namespace offset from the `index`th entry from the namespace table. + fn read_ns_offset_unchecked(&self, index: &NsIndex) -> usize { + let start = + index.0 * (NS_ID_BYTE_LEN + NS_OFFSET_BYTE_LEN) + NUM_NSS_BYTE_LEN + NS_ID_BYTE_LEN; + usize_from_bytes::(&self.bytes[start..start + NS_OFFSET_BYTE_LEN]) + } + + /// Helper for [`NsTable::validate`], used in our custom [`serde`] + /// implementation. + /// + /// Checks conditions 1-3 of [`NsTable::validate`]. Those conditions can be + /// checked by looking only at the contents of the [`NsTable`]. + fn validate_deserialization_invariants(&self) -> Result<(), NsTableValidationError> { + use NsTableValidationError::*; + + // Byte length for a table with `x` entries must be exactly `x * + // NsTableBuilder::entry_byte_len() + + // NsTableBuilder::header_byte_len()`. + // + // Explanation for the following `if` condition: + // + // The above condition is equivalent to `[byte length] - + // header_byte_len` equals 0 modulo `entry_byte_len`. In order to + // compute `[byte length] - header_byte_len` we must first check that + // `[byte length]` is not exceeded by `header_byte_len` + if self.bytes.len() < NsTableBuilder::header_byte_len() + || (self.bytes.len() - NsTableBuilder::header_byte_len()) + % NsTableBuilder::entry_byte_len() + != 0 + { + return Err(InvalidByteLen); + } + + // Header must declare the correct number of namespaces + // + // TODO this check obsolete after + // https://github.com/EspressoSystems/espresso-sequencer/issues/1604 + if self.len().0 != self.read_num_nss() { + return Err(InvalidHeader); + } + + // Namespace IDs and offsets must increase monotonically. Offsets must + // be nonzero. + { + let (mut prev_ns_id, mut prev_offset) = (None, 0); + for (ns_id, offset) in self.iter().map(|i| { + ( + self.read_ns_id_unchecked(&i), + self.read_ns_offset_unchecked(&i), + ) + }) { + if let Some(prev_ns_id) = prev_ns_id { + if ns_id <= prev_ns_id { + return Err(NonIncreasingEntries); + } + } + if offset <= prev_offset { + return Err(NonIncreasingEntries); + } + (prev_ns_id, prev_offset) = (Some(ns_id), offset); + } + } + + Ok(()) + } +} + +impl EncodeBytes for NsTable { + fn encode(&self) -> Arc<[u8]> { + Arc::from(self.bytes.as_ref()) + } +} + +impl Committable for NsTable { + fn commit(&self) -> Commitment { + RawCommitmentBuilder::new(&Self::tag()) + .var_size_bytes(&self.bytes) + .finalize() + } + + fn tag() -> String { + "NSTABLE".into() + } +} + +/// Return type for [`NsTable::validate`]. +#[derive(Error, Debug, Display, Eq, PartialEq)] +pub enum NsTableValidationError { + InvalidByteLen, + NonIncreasingEntries, + InvalidHeader, // TODO this variant obsolete after https://github.com/EspressoSystems/espresso-sequencer/issues/1604 + InvalidFinalOffset, // TODO this variant obsolete after https://github.com/EspressoSystems/espresso-sequencer/issues/1604 + ExpectNonemptyNsTable, +} + +pub struct NsTableBuilder { + bytes: Vec, + num_entries: usize, +} + +impl NsTableBuilder { + pub fn new() -> Self { + // pre-allocate space for the ns table header + Self { + bytes: Vec::from([0; NUM_NSS_BYTE_LEN]), + num_entries: 0, + } + } + + /// Add an entry to the namespace table. + pub fn append_entry(&mut self, ns_id: NamespaceId, offset: usize) { + // hack to serialize `NamespaceId` to `NS_ID_BYTE_LEN` bytes + self.bytes + .extend(u32_to_bytes::(u32::from(ns_id))); + self.bytes + .extend(usize_to_bytes::(offset)); + self.num_entries += 1; + } + + /// Serialize to bytes and consume self. + pub fn into_ns_table(self) -> NsTable { + let mut bytes = self.bytes; + // write the number of entries to the ns table header + bytes[..NUM_NSS_BYTE_LEN] + .copy_from_slice(&usize_to_bytes::(self.num_entries)); + NsTable { bytes } + } + + /// Byte length of a namespace table header. + pub const fn header_byte_len() -> usize { + NUM_NSS_BYTE_LEN + } + + /// Byte length of a single namespace table entry. + pub const fn entry_byte_len() -> usize { + NS_ID_BYTE_LEN + NS_OFFSET_BYTE_LEN + } +} + +/// Index for an entry in a ns table. +#[derive(Clone, Debug, Display, Eq, Hash, PartialEq)] +pub struct NsIndex(usize); +bytes_serde_impl!(NsIndex, to_bytes, [u8; NUM_NSS_BYTE_LEN], from_bytes); + +impl NsIndex { + pub fn to_bytes(&self) -> [u8; NUM_NSS_BYTE_LEN] { + usize_to_bytes::(self.0) + } + fn from_bytes(bytes: &[u8]) -> Self { + Self(usize_from_bytes::(bytes)) + } +} + +/// Number of entries in a namespace table. +pub struct NumNss(usize); + +impl NumNss { + pub fn in_bounds(&self, index: &NsIndex) -> bool { + index.0 < self.0 + } +} + +/// Return type for [`Payload::ns_iter`]. +pub(in crate::block) struct NsIter(Range); + +impl NsIter { + pub fn new(num_nss: &NumNss) -> Self { + Self(0..num_nss.0) + } +} + +// Simple `impl Iterator` delegates to `Range`. +impl Iterator for NsIter { + type Item = NsIndex; + + fn next(&mut self) -> Option { + self.0.next().map(NsIndex) + } +} + +#[cfg(test)] +mod test; diff --git a/sequencer/src/block/full_payload/ns_table/test.rs b/sequencer/src/block/full_payload/ns_table/test.rs new file mode 100644 index 000000000..df92d25e1 --- /dev/null +++ b/sequencer/src/block/full_payload/ns_table/test.rs @@ -0,0 +1,247 @@ +use super::{ + NsTable, NsTableBuilder, NsTableValidationError, NS_ID_BYTE_LEN, NS_OFFSET_BYTE_LEN, + NUM_NSS_BYTE_LEN, +}; +use crate::{ + block::{ + test::ValidTest, + uint_bytes::{u32_max_from_byte_len, usize_max_from_byte_len, usize_to_bytes}, + }, + NamespaceId, Payload, +}; +use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; +use hotshot::traits::BlockPayload; +use rand::{Rng, RngCore}; +use NsTableValidationError::*; + +#[test] +fn random_valid() { + setup_logging(); + setup_backtrace(); + let mut rng = jf_utils::test_rng(); + + for num_entries in 0..20 { + expect_valid(&random_valid_ns_table(num_entries, &mut rng)); + } +} + +#[test] +fn ns_table_byte_len() { + setup_logging(); + setup_backtrace(); + let mut rng = jf_utils::test_rng(); + + // Extremely small byte lengths should get rejected. + { + let mut ns_table = NsTable { bytes: Vec::new() }; + expect_invalid(&ns_table, InvalidByteLen); + expect_num_bytes_invalid(&mut ns_table, NsTableBuilder::header_byte_len(), &mut rng); + } + + // Add enough bytes for a new entry. + { + let mut ns_table = random_valid_ns_table(20, &mut rng); + expect_num_bytes_invalid(&mut ns_table, NsTableBuilder::entry_byte_len(), &mut rng); + } + + // Helper fn: add 1 byte to the `ns_table` `num_bytes` times. Expect + // invalidity in all but the final time. + fn expect_num_bytes_invalid(ns_table: &mut NsTable, num_bytes: usize, rng: &mut R) + where + R: RngCore, + { + for i in 0..num_bytes { + ns_table.bytes.push(rng.gen()); + if i == num_bytes - 1 { + break; // final iteration: no error expected + } + expect_invalid(ns_table, InvalidByteLen); + } + expect_invalid(ns_table, InvalidHeader); + } +} + +#[async_std::test] +async fn payload_byte_len() { + setup_logging(); + setup_backtrace(); + let test_case = vec![vec![5, 8, 8], vec![7, 9, 11], vec![10, 5, 8]]; + let mut rng = jf_utils::test_rng(); + let test = ValidTest::from_tx_lengths(test_case, &mut rng); + let mut block = + Payload::from_transactions(test.all_txs(), &Default::default(), &Default::default()) + .await + .unwrap() + .0; + let payload_byte_len = block.byte_len(); + let final_offset = block + .ns_table() + .read_ns_offset_unchecked(&block.ns_table().iter().last().unwrap()); + + // final offset matches payload byte len + block.ns_table().validate(&payload_byte_len).unwrap(); + + // Helper closure fn: modify the final offset of `block`'s namespace table + // by adding `diff` to it. Assert failure. + let mut modify_final_offset = |diff: isize| { + let ns_table_byte_len = block.ns_table().bytes.len(); + let old_final_offset: isize = final_offset.try_into().unwrap(); + let new_final_offset: usize = (old_final_offset + diff).try_into().unwrap(); + + block.ns_table_mut().bytes[ns_table_byte_len - NS_OFFSET_BYTE_LEN..] + .copy_from_slice(&usize_to_bytes::(new_final_offset)); + assert_eq!( + block.ns_table().validate(&payload_byte_len).unwrap_err(), + InvalidFinalOffset + ); + }; + + // final offset exceeds payload byte len + modify_final_offset(1); + + // final offset less than payload byte len + modify_final_offset(-1); + + // zero-length payload + let empty_block = Payload::from_transactions([], &Default::default(), &Default::default()) + .await + .unwrap() + .0; + assert_eq!(empty_block.ns_table().len().0, 0); + assert_eq!( + empty_block.ns_table().bytes, + usize_to_bytes::(0) + ); + empty_block + .ns_table() + .validate(&empty_block.byte_len()) + .unwrap(); + + // empty namespace table with nonempty payload + *block.ns_table_mut() = empty_block.ns_table().clone(); + assert_eq!( + block.ns_table().validate(&payload_byte_len).unwrap_err(), + ExpectNonemptyNsTable + ); +} + +#[test] +fn monotonic_increase() { + setup_logging(); + setup_backtrace(); + + // Duplicate namespace ID + two_entries_ns_table((5, 5), (5, 6), false); + + // Decreasing namespace ID + two_entries_ns_table((5, 5), (4, 6), false); + + // Duplicate offset + two_entries_ns_table((5, 5), (6, 5), false); + + // Decreasing offset + two_entries_ns_table((5, 5), (6, 4), false); + + // Zero namespace ID + two_entries_ns_table((0, 5), (6, 6), true); + + // Zero offset + two_entries_ns_table((5, 0), (6, 6), false); + + // Helper fn: build a 2-entry NsTable, assert failure + fn two_entries_ns_table(entry1: (u32, usize), entry2: (u32, usize), expect_success: bool) { + let mut ns_table_builder = NsTableBuilder::new(); + ns_table_builder.append_entry(NamespaceId::from(entry1.0), entry1.1); + ns_table_builder.append_entry(NamespaceId::from(entry2.0), entry2.1); + let ns_table = ns_table_builder.into_ns_table(); + if expect_success { + expect_valid(&ns_table); + } else { + expect_invalid(&ns_table, NonIncreasingEntries); + } + } +} + +// TODO this test obsolete after +// https://github.com/EspressoSystems/espresso-sequencer/issues/1604 +#[test] +fn header() { + setup_logging(); + setup_backtrace(); + let mut rng = jf_utils::test_rng(); + + for num_entries in 0..20 { + let mut ns_table = random_valid_ns_table(num_entries, &mut rng); + if num_entries != 0 { + set_header(&mut ns_table, 0); + set_header(&mut ns_table, num_entries - 1); + } + set_header(&mut ns_table, num_entries + 1); + set_header(&mut ns_table, usize_max_from_byte_len(NUM_NSS_BYTE_LEN)); + } + + // Helper fn: set the header of `ns_table` to declare `num_nss` entries, + // assert failure. + fn set_header(ns_table: &mut NsTable, num_nss: usize) { + ns_table.bytes[..NUM_NSS_BYTE_LEN] + .copy_from_slice(&usize_to_bytes::(num_nss)); + expect_invalid(ns_table, InvalidHeader); + } +} + +fn random_valid_ns_table(num_entries: usize, rng: &mut R) -> NsTable +where + R: RngCore, +{ + let (offset_max_increment, ns_id_max_increment) = if num_entries == 0 { + (0, 0) + } else { + let num_entries_u32: u32 = num_entries.try_into().unwrap(); + ( + usize_max_from_byte_len(NS_OFFSET_BYTE_LEN) / num_entries, + u32_max_from_byte_len(NS_ID_BYTE_LEN) / num_entries_u32, + ) + }; + + let mut ns_id = 0; + let mut offset = 0; + let mut ns_table_builder = NsTableBuilder::new(); + for _ in 0..num_entries { + // ns_id, offset must increase monotonically + ns_id += rng.gen_range(1..=ns_id_max_increment); + offset += rng.gen_range(1..=offset_max_increment); + ns_table_builder.append_entry(NamespaceId::from(ns_id), offset); + } + ns_table_builder.into_ns_table() +} + +fn expect_valid(ns_table: &NsTable) { + // `validate` should succeed + ns_table.validate_deserialization_invariants().unwrap(); + + // serde round-trip should succeed + let serde_bytes = bincode::serialize(ns_table).unwrap(); + let ns_table_serde: NsTable = bincode::deserialize(&serde_bytes).unwrap(); + assert_eq!(&ns_table_serde, ns_table); +} + +fn expect_invalid(ns_table: &NsTable, err: NsTableValidationError) { + use serde::de::Error; + + // `validate` should fail + assert_eq!( + ns_table.validate_deserialization_invariants().unwrap_err(), + err + ); + + // serde round-trip should fail + // + // need to use `to_string` because `bincode::Error`` is not `Eq` + let serde_bytes = bincode::serialize(ns_table).unwrap(); + assert_eq!( + bincode::deserialize::(&serde_bytes) + .unwrap_err() + .to_string(), + bincode::Error::custom(err).to_string(), + ); +} diff --git a/sequencer/src/block/full_payload/payload.rs b/sequencer/src/block/full_payload/payload.rs new file mode 100644 index 000000000..b0078ff09 --- /dev/null +++ b/sequencer/src/block/full_payload/payload.rs @@ -0,0 +1,319 @@ +use crate::{ + block::{ + full_payload::ns_table::{NsIndex, NsTable, NsTableBuilder}, + namespace_payload::{Index, Iter, NsPayload, NsPayloadBuilder, NsPayloadRange, TxProof}, + }, + ChainConfig, NamespaceId, NodeState, SeqTypes, Transaction, ValidatedState, +}; + +use async_trait::async_trait; +use committable::Committable; +use derive_more::Display; +use hotshot_query_service::availability::QueryablePayload; +use hotshot_types::{ + traits::{BlockPayload, EncodeBytes}, + utils::BuilderCommitment, + vid::{VidCommon, VidSchemeType}, +}; +use jf_vid::VidScheme; +use serde::{Deserialize, Serialize}; +use sha2::Digest; +use std::{collections::BTreeMap, sync::Arc}; + +/// Raw payload data for an entire block. +/// +/// A block consists of two sequences of arbitrary bytes: +/// - `ns_table`: namespace table +/// - `ns_payloads`: namespace payloads +/// +/// Any sequence of bytes is a valid `ns_table`. Any sequence of bytes is a +/// valid `ns_payloads`. The contents of `ns_table` determine how to interpret +/// `ns_payload`. +/// +/// # Namespace table +/// +/// See [`NsTable`] for the format of a namespace table. +/// +/// # Namespace payloads +/// +/// A concatenation of payload bytes for multiple individual namespaces. +/// Namespace boundaries are dictated by `ns_table`. See [`NsPayload`] for the +/// format of a namespace payload. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct Payload { + // Concatenated payload bytes for each namespace + // + // TODO want to rename thisfield to `ns_payloads`, but can't due to + // serialization compatibility. + #[serde(with = "base64_bytes")] + raw_payload: Vec, + + ns_table: NsTable, +} + +impl Payload { + pub fn ns_table(&self) -> &NsTable { + &self.ns_table + } + + /// Like [`QueryablePayload::transaction_with_proof`] except without the + /// proof. + pub fn transaction(&self, index: &Index) -> Option { + let ns_id = self.ns_table.read_ns_id(index.ns())?; + let ns_payload = self.ns_payload(index.ns()); + ns_payload.export_tx(&ns_id, index.tx()) + } + + // CRATE-VISIBLE HELPERS START HERE + + pub(in crate::block) fn read_ns_payload(&self, range: &NsPayloadRange) -> &NsPayload { + NsPayload::from_bytes_slice(&self.raw_payload[range.as_block_range()]) + } + + /// Convenience wrapper for [`Self::read_ns_payload`]. + /// + /// `index` is not checked. Use `self.ns_table().in_bounds()` as needed. + pub(in crate::block) fn ns_payload(&self, index: &NsIndex) -> &NsPayload { + let ns_payload_range = self.ns_table().ns_range(index, &self.byte_len()); + self.read_ns_payload(&ns_payload_range) + } + + pub(in crate::block) fn byte_len(&self) -> PayloadByteLen { + PayloadByteLen(self.raw_payload.len()) + } + + // PRIVATE HELPERS START HERE + + /// Need a sync version of [`BlockPayload::from_transactions`] in order to impl [`BlockPayload::empty`]. + fn from_transactions_sync( + transactions: impl IntoIterator>::Transaction> + Send, + chain_config: ChainConfig, + _instance_state: &>::Instance, + ) -> Result< + (Self, >::Metadata), + >::Error, + > { + // accounting for block byte length limit + let max_block_byte_len: usize = u64::from(chain_config.max_block_size) + .try_into() + .map_err(|_| >::Error::BlockBuilding)?; + let mut block_byte_len = NsTableBuilder::header_byte_len(); + + // add each tx to its namespace + let mut ns_builders = BTreeMap::::new(); + for tx in transactions.into_iter() { + // accounting for block byte length limit + block_byte_len += tx.payload().len() + NsPayloadBuilder::tx_table_entry_byte_len(); + if !ns_builders.contains_key(&tx.namespace()) { + // each new namespace adds overhead + block_byte_len += + NsTableBuilder::entry_byte_len() + NsPayloadBuilder::tx_table_header_byte_len(); + } + if block_byte_len > max_block_byte_len { + tracing::warn!("transactions truncated to fit in maximum block byte length {max_block_byte_len}"); + break; + } + + let ns_builder = ns_builders.entry(tx.namespace()).or_default(); + ns_builder.append_tx(tx); + } + + // build block payload and namespace table + let mut payload = Vec::new(); + let mut ns_table_builder = NsTableBuilder::new(); + for (ns_id, ns_builder) in ns_builders { + payload.extend(ns_builder.into_bytes()); + ns_table_builder.append_entry(ns_id, payload.len()); + } + let ns_table = ns_table_builder.into_ns_table(); + let metadata = ns_table.clone(); + Ok(( + Self { + raw_payload: payload, + ns_table, + }, + metadata, + )) + } +} + +#[async_trait] +impl BlockPayload for Payload { + // TODO BlockPayload trait eliminate unneeded args, return vals of type + // `Self::Metadata` https://github.com/EspressoSystems/HotShot/issues/3300 + type Error = crate::Error; + type Transaction = Transaction; + type Instance = NodeState; + type Metadata = NsTable; + type ValidatedState = ValidatedState; + + async fn from_transactions( + transactions: impl IntoIterator + Send, + validated_state: &Self::ValidatedState, + instance_state: &Self::Instance, + ) -> Result<(Self, Self::Metadata), Self::Error> { + let validated_state_cf = validated_state.chain_config; + let instance_state_cf = instance_state.chain_config; + + let chain_config = if validated_state_cf.commit() == instance_state_cf.commit() { + instance_state_cf + } else { + match validated_state_cf.resolve() { + Some(cf) => cf, + None => { + instance_state + .peers + .as_ref() + .fetch_chain_config(validated_state_cf.commit()) + .await + } + } + }; + + Self::from_transactions_sync(transactions, chain_config, instance_state) + } + + // TODO avoid cloning the entire payload here? + fn from_bytes(block_payload_bytes: &[u8], ns_table: &Self::Metadata) -> Self { + Self { + raw_payload: block_payload_bytes.to_vec(), + ns_table: ns_table.clone(), + } + } + + fn empty() -> (Self, Self::Metadata) { + let payload = Self::from_transactions_sync(vec![], Default::default(), &Default::default()) + .unwrap() + .0; + let ns_table = payload.ns_table().clone(); + (payload, ns_table) + } + + fn builder_commitment(&self, metadata: &Self::Metadata) -> BuilderCommitment { + let ns_table_bytes = self.ns_table.encode(); + + // TODO `metadata_bytes` equals `ns_table_bytes`, so we are + // double-hashing the ns_table. Why? To maintain serialization + // compatibility. + // https://github.com/EspressoSystems/espresso-sequencer/issues/1576 + let metadata_bytes = metadata.encode(); + + let mut digest = sha2::Sha256::new(); + digest.update((self.raw_payload.len() as u64).to_le_bytes()); + digest.update((ns_table_bytes.len() as u64).to_le_bytes()); + digest.update((metadata_bytes.len() as u64).to_le_bytes()); // https://github.com/EspressoSystems/espresso-sequencer/issues/1576 + digest.update(&self.raw_payload); + digest.update(ns_table_bytes); + digest.update(metadata_bytes); // https://github.com/EspressoSystems/espresso-sequencer/issues/1576 + BuilderCommitment::from_raw_digest(digest.finalize()) + } + + fn transactions<'a>( + &'a self, + metadata: &'a Self::Metadata, + ) -> impl 'a + Iterator { + self.enumerate(metadata).map(|(_, t)| t) + } +} + +impl QueryablePayload for Payload { + // TODO changes to QueryablePayload trait: + // https://github.com/EspressoSystems/hotshot-query-service/issues/639 + type TransactionIndex = Index; + type Iter<'a> = Iter<'a>; + type InclusionProof = TxProof; + + fn len(&self, _meta: &Self::Metadata) -> usize { + // Counting txs is nontrivial. The easiest solution is to consume an + // iterator. If performance is a concern then we could cache this count + // on construction of `Payload`. + self.iter(_meta).count() + } + + fn iter<'a>(&'a self, _meta: &'a Self::Metadata) -> Self::Iter<'a> { + Iter::new(self) + } + + fn transaction_with_proof( + &self, + _meta: &Self::Metadata, + index: &Self::TransactionIndex, + ) -> Option<(Self::Transaction, Self::InclusionProof)> { + // TODO HACK! THE RETURNED PROOF MIGHT FAIL VERIFICATION. + // https://github.com/EspressoSystems/hotshot-query-service/issues/639 + // + // Need a `VidCommon` to proceed. Need to modify `QueryablePayload` + // trait to add a `VidCommon` arg. In the meantime tests fail if I leave + // it `todo!()`, so this hack allows tests to pass. + let common = hotshot_types::vid::vid_scheme(10) + .disperse(&self.raw_payload) + .unwrap() + .common; + + TxProof::new(index, self, &common) + } +} + +impl std::fmt::Display for Payload { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:#?}") + } +} + +impl EncodeBytes for Payload { + fn encode(&self) -> Arc<[u8]> { + Arc::from(self.raw_payload.as_ref()) + } +} + +/// Byte length of a block payload, which includes all namespaces but *not* the +/// namespace table. +#[derive(Clone, Debug, Display, Eq, Hash, PartialEq)] +pub struct PayloadByteLen(usize); + +impl PayloadByteLen { + /// Extract payload byte length from a [`VidCommon`] and construct a new [`Self`] from it. + pub fn from_vid_common(common: &VidCommon) -> Self { + Self(usize::try_from(VidSchemeType::get_payload_byte_len(common)).unwrap()) + } + + /// Is the payload byte length declared in a [`VidCommon`] equal [`Self`]? + pub fn is_consistent(&self, common: &VidCommon) -> bool { + // failure to convert to usize implies that `common` cannot be + // consistent with `self`. + let expected = match usize::try_from(VidSchemeType::get_payload_byte_len(common)) { + Ok(n) => n, + Err(_) => { + tracing::warn!( + "VidCommon byte len u32 {} should convert to usize", + VidSchemeType::get_payload_byte_len(common) + ); + return false; + } + }; + + self.0 == expected + } + + pub(in crate::block::full_payload) fn as_usize(&self) -> usize { + self.0 + } +} + +#[cfg(any(test, feature = "testing"))] +impl hotshot_types::traits::block_contents::TestableBlock for Payload { + fn genesis() -> Self { + BlockPayload::empty().0 + } + + fn txn_count(&self) -> u64 { + self.len(&self.ns_table) as u64 + } +} + +#[cfg(any(test, feature = "testing"))] +impl Payload { + pub fn ns_table_mut(&mut self) -> &mut NsTable { + &mut self.ns_table + } +} diff --git a/sequencer/src/block/namespace_payload.rs b/sequencer/src/block/namespace_payload.rs new file mode 100644 index 000000000..ecd894f86 --- /dev/null +++ b/sequencer/src/block/namespace_payload.rs @@ -0,0 +1,12 @@ +mod iter; +mod ns_payload; +mod ns_payload_range; +mod tx_proof; +mod types; + +pub use iter::{Index, Iter}; +pub use tx_proof::TxProof; + +pub(in crate::block) use ns_payload::{NsPayload, NsPayloadOwned}; +pub(in crate::block) use ns_payload_range::NsPayloadRange; +pub(in crate::block) use types::NsPayloadBuilder; diff --git a/sequencer/src/block/namespace_payload/iter.rs b/sequencer/src/block/namespace_payload/iter.rs new file mode 100644 index 000000000..09da31b9a --- /dev/null +++ b/sequencer/src/block/namespace_payload/iter.rs @@ -0,0 +1,81 @@ +use crate::block::{ + full_payload::{NsIndex, NsIter, Payload}, + namespace_payload::types::{TxIndex, TxIter}, +}; +use serde::{Deserialize, Serialize}; +use std::iter::Peekable; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Index { + ns_index: NsIndex, + tx_index: TxIndex, +} + +impl Index { + pub fn ns(&self) -> &NsIndex { + &self.ns_index + } + pub(in crate::block) fn tx(&self) -> &TxIndex { + &self.tx_index + } +} + +// TODO don't impl `PartialOrd` +// It's needed only for `QueryablePayload` trait: +// https://github.com/EspressoSystems/hotshot-query-service/issues/639 +impl PartialOrd for Index { + fn partial_cmp(&self, _other: &Self) -> Option { + Some(self.cmp(_other)) + } +} +// TODO don't impl `Ord` +// It's needed only for `QueryablePayload` trait: +// https://github.com/EspressoSystems/hotshot-query-service/issues/639 +impl Ord for Index { + fn cmp(&self, _other: &Self) -> std::cmp::Ordering { + unimplemented!() + } +} + +/// Cartesian product of [`NsIter`], [`TxIter`]. +pub struct Iter<'a> { + ns_iter: Peekable, + tx_iter: Option, + block: &'a Payload, +} + +impl<'a> Iter<'a> { + pub fn new(block: &'a Payload) -> Self { + Self { + ns_iter: NsIter::new(&block.ns_table().len()).peekable(), + tx_iter: None, + block, + } + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = Index; + + fn next(&mut self) -> Option { + loop { + let Some(ns_index) = self.ns_iter.peek() else { + break None; // ns_iter consumed + }; + + if let Some(tx_index) = self + .tx_iter + .get_or_insert_with(|| self.block.ns_payload(ns_index).iter()) + .next() + { + break Some(Index { + ns_index: ns_index.clone(), + tx_index, + }); + } + + self.tx_iter = None; // unset `tx_iter`; it's consumed for this namespace + self.ns_iter.next(); + } + } +} diff --git a/sequencer/src/block/namespace_payload/ns_payload.rs b/sequencer/src/block/namespace_payload/ns_payload.rs new file mode 100644 index 000000000..f2997839d --- /dev/null +++ b/sequencer/src/block/namespace_payload/ns_payload.rs @@ -0,0 +1,137 @@ +use crate::{ + block::namespace_payload::types::{ + FromNsPayloadBytes, NsPayloadByteLen, NsPayloadBytesRange, NumTxs, NumTxsRange, + NumTxsUnchecked, TxIndex, TxIter, TxPayloadRange, TxTableEntriesRange, + }, + NamespaceId, Transaction, +}; +use serde::{Deserialize, Serialize}; + +/// Raw binary data for a single namespace's payload. +/// +/// Any sequence of bytes is a valid [`NsPayload`]. +/// +/// See module-level documentation [`types`](super::types) for a full +/// specification of the binary format of a namespace. +pub(in crate::block) struct NsPayload([u8]); + +impl NsPayload { + pub fn from_bytes_slice(bytes: &[u8]) -> &NsPayload { + NsPayload::new_private(bytes) + } + pub fn as_bytes_slice(&self) -> &[u8] { + &self.0 + } + pub fn byte_len(&self) -> NsPayloadByteLen { + NsPayloadByteLen::from_usize(self.0.len()) + } + + /// Read and parse bytes from the ns payload. + /// + /// Arg `range: &R` is convertible into a `Range` via + /// [`NsPayloadBytesRange`]. The payload bytes are parsed into a `R::Output` + /// via [`FromNsPayloadBytes`]. + pub fn read<'a, R>(&'a self, range: &R) -> R::Output + where + R: NsPayloadBytesRange<'a>, + { + >::from_payload_bytes(&self.0[range.ns_payload_range()]) + } + + /// Iterator over all transactions in this namespace. + pub fn iter(&self) -> TxIter { + self.iter_from_num_txs(&self.read_num_txs()) + } + + /// Return all transactions in this namespace. The namespace ID for each + /// returned [`Transaction`] is set to `ns_id`. + pub fn export_all_txs(&self, ns_id: &NamespaceId) -> Vec { + let num_txs = self.read_num_txs(); + self.iter_from_num_txs(&num_txs) + .map(|i| self.tx_from_num_txs(ns_id, &i, &num_txs)) + .collect() + } + + /// Return a transaction from this namespace. Set its namespace ID to + /// `ns_id`. + /// + /// Return `None` if `index` is out of bounds. + pub fn export_tx(&self, ns_id: &NamespaceId, index: &TxIndex) -> Option { + let num_txs_unchecked = self.read_num_txs(); + let num_txs = NumTxs::new(&num_txs_unchecked, &self.byte_len()); + if !num_txs.in_bounds(index) { + return None; // error: tx index out of bounds + } + Some(self.tx_from_num_txs(ns_id, index, &num_txs_unchecked)) + } + + /// Private helper. (Could be pub if desired.) + fn read_num_txs(&self) -> NumTxsUnchecked { + self.read(&NumTxsRange::new(&self.byte_len())) + } + + /// Private helper + fn iter_from_num_txs(&self, num_txs: &NumTxsUnchecked) -> TxIter { + let num_txs = NumTxs::new(num_txs, &self.byte_len()); + TxIter::new(&num_txs) + } + + /// Private helper + fn tx_from_num_txs( + &self, + ns_id: &NamespaceId, + index: &TxIndex, + num_txs_unchecked: &NumTxsUnchecked, + ) -> Transaction { + let tx_table_entries = self.read(&TxTableEntriesRange::new(index)); + let tx_range = TxPayloadRange::new(num_txs_unchecked, &tx_table_entries, &self.byte_len()); + + // TODO don't copy the tx bytes into the return value + // https://github.com/EspressoSystems/hotshot-query-service/issues/267 + let tx_payload = self.read(&tx_range).to_payload_bytes().to_vec(); + Transaction::new(*ns_id, tx_payload) + } +} + +#[repr(transparent)] +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(transparent)] +pub(in crate::block) struct NsPayloadOwned(#[serde(with = "base64_bytes")] Vec); + +/// Crazy boilerplate code to make it so that [`NsPayloadOwned`] is to +/// [`NsPayload`] as [`Vec`] is to `[T]`. See [How can I create newtypes for +/// an unsized type and its owned counterpart (like `str` and `String`) in safe +/// Rust? - Stack Overflow](https://stackoverflow.com/q/64977525) +mod ns_payload_owned { + use super::{NsPayload, NsPayloadOwned}; + use std::borrow::Borrow; + use std::ops::Deref; + + impl NsPayload { + // pub(super) because I want it visible everywhere in this file but I + // also want this boilerplate code quarrantined in `ns_payload_owned`. + pub(super) fn new_private(p: &[u8]) -> &NsPayload { + unsafe { &*(p as *const [u8] as *const NsPayload) } + } + } + + impl Deref for NsPayloadOwned { + type Target = NsPayload; + fn deref(&self) -> &NsPayload { + NsPayload::new_private(&self.0) + } + } + + impl Borrow for NsPayloadOwned { + fn borrow(&self) -> &NsPayload { + self.deref() + } + } + + impl ToOwned for NsPayload { + type Owned = NsPayloadOwned; + fn to_owned(&self) -> NsPayloadOwned { + NsPayloadOwned(self.0.to_owned()) + } + } +} diff --git a/sequencer/src/block/namespace_payload/ns_payload_range.rs b/sequencer/src/block/namespace_payload/ns_payload_range.rs new file mode 100644 index 000000000..f2812f6fd --- /dev/null +++ b/sequencer/src/block/namespace_payload/ns_payload_range.rs @@ -0,0 +1,34 @@ +use super::types::{NsPayloadByteLen, NsPayloadBytesRange}; +use std::ops::Range; + +/// Index range for a namespace payload inside a block payload. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub(in crate::block) struct NsPayloadRange(Range); + +impl NsPayloadRange { + /// TODO restrict visibility? + pub fn new(start: usize, end: usize) -> Self { + Self(start..end) + } + + /// Access the underlying index range for this namespace inside a block + /// payload. + pub fn as_block_range(&self) -> Range { + self.0.clone() + } + + /// Return the byte length of this namespace. + pub fn byte_len(&self) -> NsPayloadByteLen { + NsPayloadByteLen::from_usize(self.0.len()) + } + + /// Convert a [`NsPayloadBytesRange`] into a range that's relative to the + /// entire block payload. + pub fn block_range<'a, R>(&self, range: &R) -> Range + where + R: NsPayloadBytesRange<'a>, + { + let range = range.ns_payload_range(); + range.start + self.0.start..range.end + self.0.start + } +} diff --git a/sequencer/src/block/namespace_payload/tx_proof.rs b/sequencer/src/block/namespace_payload/tx_proof.rs new file mode 100644 index 000000000..ee025c0f4 --- /dev/null +++ b/sequencer/src/block/namespace_payload/tx_proof.rs @@ -0,0 +1,253 @@ +use crate::{ + block::{ + full_payload::{ + NsTable, {Payload, PayloadByteLen}, + }, + namespace_payload::{ + iter::Index, + types::{ + NumTxs, NumTxsRange, NumTxsUnchecked, TxIndex, TxPayloadRange, TxTableEntries, + TxTableEntriesRange, + }, + }, + }, + Transaction, +}; +use hotshot_query_service::{VidCommitment, VidCommon}; +use hotshot_types::{ + traits::EncodeBytes, + vid::{vid_scheme, SmallRangeProofType, VidSchemeType}, +}; +use jf_vid::{ + payload_prover::{PayloadProver, Statement}, + VidScheme, +}; +use serde::{Deserialize, Serialize}; + +/// Proof of correctness for transaction bytes in a block. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct TxProof { + // Naming conventions for this struct's fields: + // - `payload_x`: bytes from the payload + // - `payload_proof_x`: a proof of those bytes from the payload + tx_index: TxIndex, + + // Number of txs declared in the tx table + payload_num_txs: NumTxsUnchecked, + payload_proof_num_txs: SmallRangeProofType, + + // Tx table entries for this tx + payload_tx_table_entries: TxTableEntries, + payload_proof_tx_table_entries: SmallRangeProofType, + + // This tx's payload bytes. + // `None` if this tx has zero length. + payload_proof_tx: Option, +} + +impl TxProof { + /// Returns the [`Transaction`] indicated by `index`, along with a proof of + /// correctness for that transaction. Returns `None` on error. + pub fn new( + index: &Index, + payload: &Payload, + common: &VidCommon, + ) -> Option<(Transaction, Self)> { + let payload_byte_len = payload.byte_len(); + if !payload_byte_len.is_consistent(common) { + tracing::warn!( + "payload byte len {} inconsistent with common {}", + payload_byte_len, + VidSchemeType::get_payload_byte_len(common) + ); + return None; // error: payload byte len inconsistent with common + } + if !payload.ns_table().in_bounds(index.ns()) { + tracing::warn!("ns_index {:?} out of bounds", index.ns()); + return None; // error: ns index out of bounds + } + // check tx index below + + let payload_bytes_arc = payload.encode(); // pacify borrow checker + let payload_bytes = payload_bytes_arc.as_ref(); + let ns_range = payload.ns_table().ns_range(index.ns(), &payload_byte_len); + let ns_byte_len = ns_range.byte_len(); + let ns_payload = payload.read_ns_payload(&ns_range); + let vid = vid_scheme( + VidSchemeType::get_num_storage_nodes(common) + .try_into() + .unwrap(), + ); + + // Read the tx table len from this namespace's tx table and compute a + // proof of correctness. + let num_txs_range = NumTxsRange::new(&ns_byte_len); + let payload_num_txs = ns_payload.read(&num_txs_range); + + // Check tx index. + // + // TODO the next line of code (and other code) could be easier to read + // if we make a helpers that repeat computation we've already done. + if !NumTxs::new(&payload_num_txs, &ns_byte_len).in_bounds(index.tx()) { + return None; // error: tx index out of bounds + } + + let payload_proof_num_txs = vid + .payload_proof(payload_bytes, ns_range.block_range(&num_txs_range)) + .ok()?; + + // Read the tx table entries for this tx and compute a proof of + // correctness. + let tx_table_entries_range = TxTableEntriesRange::new(index.tx()); + let payload_tx_table_entries = ns_payload.read(&tx_table_entries_range); + let payload_proof_tx_table_entries = { + vid.payload_proof(payload_bytes, ns_range.block_range(&tx_table_entries_range)) + .ok()? + }; + + // Read the tx payload and compute a proof of correctness. + let tx_payload_range = + TxPayloadRange::new(&payload_num_txs, &payload_tx_table_entries, &ns_byte_len); + let payload_proof_tx = { + let range = ns_range.block_range(&tx_payload_range); + if range.is_empty() { + None + } else { + Some(vid.payload_proof(payload_bytes, range).ok()?) + } + }; + + let tx = { + let ns_id = payload.ns_table().read_ns_id_unchecked(index.ns()); + let tx_payload = ns_payload + .read(&tx_payload_range) + .to_payload_bytes() + .to_vec(); + Transaction::new(ns_id, tx_payload) + }; + + Some(( + tx, + TxProof { + tx_index: index.tx().clone(), + payload_num_txs, + payload_proof_num_txs, + payload_tx_table_entries, + payload_proof_tx_table_entries, + payload_proof_tx, + }, + )) + } + + /// Verify a [`TxProof`] for `tx` against a payload commitment. Returns + /// `None` on error. + pub fn verify( + &self, + ns_table: &NsTable, + tx: &Transaction, + commit: &VidCommitment, + common: &VidCommon, + ) -> Option { + VidSchemeType::is_consistent(commit, common).ok()?; + let Some(ns_index) = ns_table.find_ns_id(&tx.namespace()) else { + tracing::info!("ns id {} does not exist", tx.namespace()); + return None; // error: ns id does not exist + }; + let ns_range = ns_table.ns_range(&ns_index, &PayloadByteLen::from_vid_common(common)); + let ns_byte_len = ns_range.byte_len(); + + if !NumTxs::new(&self.payload_num_txs, &ns_byte_len).in_bounds(&self.tx_index) { + tracing::info!("tx index {:?} out of bounds", self.tx_index); + return None; // error: tx index out of bounds + } + + let vid = vid_scheme( + VidSchemeType::get_num_storage_nodes(common) + .try_into() + .unwrap(), + ); + + // Verify proof for tx table len + { + let range = ns_range.block_range(&NumTxsRange::new(&ns_byte_len)); + if vid + .payload_verify( + Statement { + payload_subslice: &self.payload_num_txs.to_payload_bytes(), + range, + commit, + common, + }, + &self.payload_proof_num_txs, + ) + .ok()? + .is_err() + { + return Some(false); + } + } + + // Verify proof for tx table entries + { + let range = ns_range.block_range(&TxTableEntriesRange::new(&self.tx_index)); + if vid + .payload_verify( + Statement { + payload_subslice: &self.payload_tx_table_entries.to_payload_bytes(), + range, + commit, + common, + }, + &self.payload_proof_tx_table_entries, + ) + .ok()? + .is_err() + { + return Some(false); + } + } + + // Verify proof for tx payload + { + let range = ns_range.block_range(&TxPayloadRange::new( + &self.payload_num_txs, + &self.payload_tx_table_entries, + &ns_byte_len, + )); + + match (&self.payload_proof_tx, range.is_empty()) { + (Some(proof), false) => { + if vid + .payload_verify( + Statement { + payload_subslice: tx.payload(), + range, + commit, + common, + }, + proof, + ) + .ok()? + .is_err() + { + return Some(false); + } + } + (None, true) => {} // 0-length tx, nothing to verify + (None, false) => { + tracing::error!( + "tx verify: missing proof for nonempty tx payload range {:?}", + range + ); + return None; + } + (Some(_), true) => { + tracing::error!("tx verify: unexpected proof for empty tx payload range"); + return None; + } + } + } + + Some(true) + } +} diff --git a/sequencer/src/block/namespace_payload/types.rs b/sequencer/src/block/namespace_payload/types.rs new file mode 100644 index 000000000..09860f80b --- /dev/null +++ b/sequencer/src/block/namespace_payload/types.rs @@ -0,0 +1,429 @@ +//! Types related to a namespace payload and its transaction table. +//! +//! All code that needs to know the binary format of a namespace payload and its +//! transaction table is restricted to this file. +//! +//! There are many newtypes in this file to facilitate transaction proofs. +//! +//! # Binary format of a namespace payload +//! +//! Any sequence of bytes is a valid [`NsPayload`]. +//! +//! A namespace payload consists of two concatenated byte sequences: +//! - `tx_table`: transaction table +//! - `tx_payloads`: transaction payloads +//! +//! # Transaction table +//! +//! Byte lengths for the different items that could appear in a `tx_table` are +//! specified in local private constants [`NUM_TXS_BYTE_LEN`], +//! [`TX_OFFSET_BYTE_LEN`]. +//! +//! ## Number of entries in the transaction table +//! +//! The first [`NUM_TXS_BYTE_LEN`] bytes of the `tx_table` indicate the number +//! `n` of entries in the table as a little-endian unsigned integer. If the +//! entire namespace payload byte length is smaller than [`NUM_TXS_BYTE_LEN`] +//! then the missing bytes are zero-padded. +//! +//! The bytes in the namespace payload beyond the first [`NUM_TXS_BYTE_LEN`] +//! bytes encode entries in the `tx_table`. Each entry consumes exactly +//! [`TX_OFFSET_BYTE_LEN`] bytes. +//! +//! The number `n` could be anything, including a number much larger than the +//! number of entries that could fit in the namespace payload. As such, the +//! actual number of entries in the `tx_table` is defined as the minimum of `n` +//! and the maximum number of whole `tx_table` entries that could fit in the +//! namespace payload. +//! +//! The `tx_payloads` consist of any bytes in the namespace payload beyond the +//! `tx_table`. +//! +//! ## Transaction table entry +//! +//! Each entry in the `tx_table` is exactly [`TX_OFFSET_BYTE_LEN`] bytes. These +//! bytes indicate the end-index of a transaction in the namespace payload +//! bytes. This end-index is a little-endian unsigned integer. +//! +//! This offset is relative to the end of the `tx_table` within the current +//! namespace. +//! +//! ### Example +//! +//! Suppose a block payload has 3000 bytes and 3 namespaces of 1000 bytes each. +//! Suppose the `tx_table` for final namespace in the block has byte length 100, +//! and suppose an entry in that `tx_table` indicates an end-index of `10`. The +//! actual end-index of that transaction relative to the current namespace is +//! `110`: `10` bytes for the offset plus `100` bytes for the `tx_table`. +//! Relative to the entire block payload, the end-index of that transaction is +//! `2110`: `10` bytes for the offset plus `100` bytes for the `tx_table` plus +//! `2000` bytes for this namespace. +//! +//! # How to deduce a transaction's byte range +//! +//! In order to extract the payload bytes of a single transaction `T` from the +//! namespace payload one needs both the start- and end-indices for `T`. +//! +//! See [`TxPayloadRange::new`] for clarification. What follows is a description +//! of what's implemented in [`TxPayloadRange::new`]. +//! +//! If `T` occupies the `i`th entry in the `tx_table` for `i>0` then the +//! start-index for `T` is defined as the end-index of the `(i-1)`th entry in +//! the table. +//! +//! Thus, both start- and end-indices for any transaction `T` can be read from a +//! contiguous, constant-size byte range in the `tx_table`. This property +//! facilitates transaction proofs. +//! +//! The start-index of the 0th entry in the table is implicitly defined to be +//! `0`. +//! +//! The start- and end-indices `(declared_start, declared_end)` declared in the +//! `tx_table` could be anything. As such, the actual start- and end-indices +//! `(start, end)` are defined so as to ensure that the byte range is +//! well-defined and in-bounds for the namespace payload: +//! ```ignore +//! end = min(declared_end, namespace_payload_byte_length) +//! start = min(declared_start, end) +//! ``` +//! +//! To get the byte range for `T` relative to the current namespace, the above +//! range is translated by the byte length of the `tx_table` *as declared in the +//! `tx_table` itself*, suitably truncated to fit within the current namespace. +//! +//! In particular, if the `tx_table` declares a huge number `n` of entries that +//! cannot fit into the namespace payload then all transactions in this +//! namespace have a zero-length byte range whose start- and end-indices are +//! both `namespace_payload_byte_length`. +//! +//! In a "honestly-prepared" `tx_table` the end-index of the final transaction +//! equals the byte length of the namespace payload minus the byte length of the +//! `tx_table`. (Otherwise the namespace payload might have bytes that are not +//! included in any transaction.) +//! +//! It is possible that a `tx_table` table could indicate two distinct +//! transactions whose byte ranges overlap, though no "honestly-prepared" +//! `tx_table` would do this. +use crate::block::uint_bytes::{bytes_serde_impl, usize_from_bytes, usize_to_bytes}; +use crate::Transaction; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::ops::Range; + +/// Byte lengths for the different items that could appear in a tx table. +const NUM_TXS_BYTE_LEN: usize = 4; +const TX_OFFSET_BYTE_LEN: usize = 4; + +/// Data that can be deserialized from a subslice of namespace payload bytes. +/// +/// Companion trait for [`NsPayloadBytesRange`], which specifies the subslice of +/// namespace payload bytes to read. +pub trait FromNsPayloadBytes<'a> { + /// Deserialize `Self` from namespace payload bytes. + fn from_payload_bytes(bytes: &'a [u8]) -> Self; +} + +/// Specifies a subslice of namespace payload bytes to read. +/// +/// Companion trait for [`FromNsPayloadBytes`], which holds data that can be +/// deserialized from that subslice of bytes. +pub trait NsPayloadBytesRange<'a> { + type Output: FromNsPayloadBytes<'a>; + + /// Range relative to this ns payload + fn ns_payload_range(&self) -> Range; +} + +/// Number of txs in a namespace. +/// +/// Like [`NumTxsUnchecked`] but checked against a [`NsPayloadByteLen`]. +pub struct NumTxs(usize); + +impl NumTxs { + /// Returns the minimum of: + /// - `num_txs` + /// - The maximum number of tx table entries that could fit in a namespace + /// whose byte length is `byte_len`. + pub fn new(num_txs: &NumTxsUnchecked, byte_len: &NsPayloadByteLen) -> Self { + Self(std::cmp::min( + // Number of txs declared in the tx table + num_txs.0, + // Max number of tx table entries that could fit in the namespace payload + byte_len.0.saturating_sub(NUM_TXS_BYTE_LEN) / TX_OFFSET_BYTE_LEN, + )) + } + + pub fn in_bounds(&self, index: &TxIndex) -> bool { + index.0 < self.0 + } +} + +/// Byte length of a namespace payload. +pub struct NsPayloadByteLen(usize); + +impl NsPayloadByteLen { + // TODO restrict visibility? + pub fn from_usize(n: usize) -> Self { + Self(n) + } +} + +/// The part of a tx table that declares the number of txs in the payload. +/// +/// "Unchecked" because this quantity might exceed the number of tx table +/// entries that could fit into the namespace that contains it. +/// +/// Use [`NumTxs`] for the actual number of txs in this namespace. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct NumTxsUnchecked(usize); +bytes_serde_impl!( + NumTxsUnchecked, + to_payload_bytes, + [u8; NUM_TXS_BYTE_LEN], + from_payload_bytes +); + +impl NumTxsUnchecked { + pub fn to_payload_bytes(&self) -> [u8; NUM_TXS_BYTE_LEN] { + usize_to_bytes::(self.0) + } +} + +impl FromNsPayloadBytes<'_> for NumTxsUnchecked { + fn from_payload_bytes(bytes: &[u8]) -> Self { + Self(usize_from_bytes::(bytes)) + } +} + +/// Byte range for the part of a tx table that declares the number of txs in the +/// payload. +pub struct NumTxsRange(Range); + +impl NumTxsRange { + pub fn new(byte_len: &NsPayloadByteLen) -> Self { + Self(0..NUM_TXS_BYTE_LEN.min(byte_len.0)) + } +} + +impl NsPayloadBytesRange<'_> for NumTxsRange { + type Output = NumTxsUnchecked; + + fn ns_payload_range(&self) -> Range { + self.0.clone() + } +} + +/// Entries from a tx table in a namespace for use in a transaction proof. +/// +/// Contains either one or two entries according to whether it was derived from +/// the first transaction in the namespace. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TxTableEntries { + cur: usize, + prev: Option, // `None` if derived from the first transaction +} + +// This serde impl uses Vec. We could save space by using an array of +// length `TWO_ENTRIES_BYTE_LEN`, but then we need a way to distinguish +// `prev=Some(0)` from `prev=None`. +bytes_serde_impl!( + TxTableEntries, + to_payload_bytes, + Vec, + from_payload_bytes +); + +impl TxTableEntries { + const TWO_ENTRIES_BYTE_LEN: usize = 2 * TX_OFFSET_BYTE_LEN; + + pub fn to_payload_bytes(&self) -> Vec { + let mut bytes = Vec::with_capacity(Self::TWO_ENTRIES_BYTE_LEN); + if let Some(prev) = self.prev { + bytes.extend(usize_to_bytes::(prev)); + } + bytes.extend(usize_to_bytes::(self.cur)); + bytes + } +} + +impl FromNsPayloadBytes<'_> for TxTableEntries { + fn from_payload_bytes(bytes: &[u8]) -> Self { + match bytes.len() { + TX_OFFSET_BYTE_LEN => Self { + cur: usize_from_bytes::(bytes), + prev: None, + }, + Self::TWO_ENTRIES_BYTE_LEN => Self { + cur: usize_from_bytes::(&bytes[TX_OFFSET_BYTE_LEN..]), + prev: Some(usize_from_bytes::( + &bytes[..TX_OFFSET_BYTE_LEN], + )), + }, + len => panic!( + "unexpected bytes len {} should be either {} or {}", + len, + TX_OFFSET_BYTE_LEN, + Self::TWO_ENTRIES_BYTE_LEN + ), + } + } +} + +/// Byte range for entries from a tx table for use in a transaction proof. +/// +/// This range covers either one or two entries from a tx table according to +/// whether it was derived from the first transaction in the namespace. +pub struct TxTableEntriesRange(Range); + +impl TxTableEntriesRange { + pub fn new(index: &TxIndex) -> Self { + let start = if index.0 == 0 { + // Special case: the desired range includes only one entry from + // the tx table: the first entry. This entry starts immediately + // following the bytes that encode the tx table length. + NUM_TXS_BYTE_LEN + } else { + // The desired range starts at the beginning of the previous tx + // table entry. + (index.0 - 1) + .saturating_mul(TX_OFFSET_BYTE_LEN) + .saturating_add(NUM_TXS_BYTE_LEN) + }; + // The desired range ends at the end of this transaction's tx table entry + let end = index + .0 + .saturating_add(1) + .saturating_mul(TX_OFFSET_BYTE_LEN) + .saturating_add(NUM_TXS_BYTE_LEN); + Self(start..end) + } +} + +impl NsPayloadBytesRange<'_> for TxTableEntriesRange { + type Output = TxTableEntries; + + fn ns_payload_range(&self) -> Range { + self.0.clone() + } +} + +/// A transaction's payload data. +pub struct TxPayload<'a>(&'a [u8]); + +impl<'a> TxPayload<'a> { + pub fn to_payload_bytes(&self) -> &'a [u8] { + self.0 + } +} + +impl<'a> FromNsPayloadBytes<'a> for TxPayload<'a> { + fn from_payload_bytes(bytes: &'a [u8]) -> Self { + Self(bytes) + } +} + +/// Byte range for a transaction's payload data. +pub struct TxPayloadRange(Range); + +impl TxPayloadRange { + pub fn new( + num_txs: &NumTxsUnchecked, + tx_table_entries: &TxTableEntries, + byte_len: &NsPayloadByteLen, + ) -> Self { + let tx_table_byte_len = num_txs + .0 + .saturating_mul(TX_OFFSET_BYTE_LEN) + .saturating_add(NUM_TXS_BYTE_LEN); + let end = tx_table_entries + .cur + .saturating_add(tx_table_byte_len) + .min(byte_len.0); + let start = tx_table_entries + .prev + .unwrap_or(0) + .saturating_add(tx_table_byte_len) + .min(end); + Self(start..end) + } +} + +impl<'a> NsPayloadBytesRange<'a> for TxPayloadRange { + type Output = TxPayload<'a>; + + fn ns_payload_range(&self) -> Range { + self.0.clone() + } +} + +/// Index for an entry in a tx table. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub(in crate::block) struct TxIndex(usize); +bytes_serde_impl!(TxIndex, to_bytes, [u8; NUM_TXS_BYTE_LEN], from_bytes); + +impl TxIndex { + pub fn to_bytes(&self) -> [u8; NUM_TXS_BYTE_LEN] { + usize_to_bytes::(self.0) + } + fn from_bytes(bytes: &[u8]) -> Self { + Self(usize_from_bytes::(bytes)) + } +} + +pub(in crate::block) struct TxIter(Range); + +impl TxIter { + pub fn new(num_txs: &NumTxs) -> Self { + Self(0..num_txs.0) + } +} + +// Simple `impl Iterator` delegates to `Range`. +impl Iterator for TxIter { + type Item = TxIndex; + + fn next(&mut self) -> Option { + self.0.next().map(TxIndex) + } +} + +/// Build an individual namespace payload one transaction at a time. +/// +/// Use [`Self::append_tx`] to add each transaction. Use [`Self::into_bytes`] +/// when you're done. The returned bytes include a well-formed tx table and all +/// tx payloads. +#[derive(Default)] +pub(in crate::block) struct NsPayloadBuilder { + tx_table_entries: Vec, + tx_bodies: Vec, +} + +impl NsPayloadBuilder { + /// Add a transaction's payload to this namespace + pub fn append_tx(&mut self, tx: Transaction) { + self.tx_bodies.extend(tx.into_payload()); + self.tx_table_entries + .extend(usize_to_bytes::(self.tx_bodies.len())); + } + + /// Serialize to bytes and consume self. + pub fn into_bytes(self) -> Vec { + let mut result = Vec::with_capacity( + NUM_TXS_BYTE_LEN + self.tx_table_entries.len() + self.tx_bodies.len(), + ); + let num_txs = NumTxsUnchecked(self.tx_table_entries.len() / TX_OFFSET_BYTE_LEN); + result.extend(num_txs.to_payload_bytes()); + result.extend(self.tx_table_entries); + result.extend(self.tx_bodies); + result + } + + /// Byte length of a tx table header. + pub const fn tx_table_header_byte_len() -> usize { + NUM_TXS_BYTE_LEN + } + + /// Byte length of a single tx table entry. + pub const fn tx_table_entry_byte_len() -> usize { + TX_OFFSET_BYTE_LEN + } +} diff --git a/sequencer/src/block/payload.rs b/sequencer/src/block/payload.rs deleted file mode 100644 index 40c083d38..000000000 --- a/sequencer/src/block/payload.rs +++ /dev/null @@ -1,1313 +0,0 @@ -use crate::block::entry::{TxTableEntry, TxTableEntryWord}; -use crate::block::payload; -use crate::{BlockBuildingSnafu, Error, NamespaceId, NodeState, Transaction}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use derivative::Derivative; -use hotshot::traits::BlockPayload; -use hotshot_types::vid::{ - vid_scheme, LargeRangeProofType, VidCommitment, VidCommon, VidSchemeType, -}; -use jf_primitives::vid::{ - payload_prover::{PayloadProver, Statement}, - VidScheme, -}; -use num_traits::PrimInt; -use serde::{Deserialize, Serialize}; -use snafu::OptionExt; -use std::default::Default; -use std::sync::Arc; -use std::{collections::HashMap, fmt::Display}; - -use crate::block::tables::NameSpaceTable; -use trait_set::trait_set; - -use crate::block::tables::TxTable; - -trait_set! { - - pub trait TableWordTraits = CanonicalSerialize - + CanonicalDeserialize - + TryFrom - + TryInto - + Default - + PrimInt - + std::marker::Sync; - - // Note: this trait is not used yet as for now the Payload structs are only parametrized with the TableWord parameter. - pub trait OffsetTraits = CanonicalSerialize - + CanonicalDeserialize - + TryFrom - + TryInto - + Default - + std::marker::Sync; - - // Note: this trait is not used yet as for now the Payload structs are only parametrized with the TableWord parameter. - pub trait NsIdTraits =CanonicalSerialize + CanonicalDeserialize + Default + std::marker::Sync; -} -pub(super) struct NamespaceInfo { - // `tx_table` is a bytes representation of the following table: - // word[0]: [number n of entries in tx table] - // word[j>0]: [end byte index of the (j-1)th tx in the payload] - // - // Thus, the ith tx payload bytes range is word[i-1]..word[i]. - // Edge case: tx_table[-1] is implicitly 0. - // - // Word type is `TxTableEntry`. - // - // TODO final entry should be implicit: - // https://github.com/EspressoSystems/espresso-sequencer/issues/757 - pub(crate) tx_table: Vec, - pub(crate) tx_bodies: Vec, // concatenation of all tx payloads - pub(crate) tx_bytes_end: TxTableEntry, // TODO make this field a usize instead - pub(crate) tx_table_len: TxTableEntry, // TODO make this field a usize instead -} - -#[allow(dead_code)] // TODO temporary -#[derive(Clone, Debug, Derivative, Deserialize, Eq, Serialize)] -#[derivative(Hash, PartialEq)] -// TODO remove the generic type param, use local constants instead -pub struct Payload { - // Sequence of bytes representing the concatenated payloads for each namespace - #[serde(with = "base64_bytes")] - pub(super) raw_payload: Vec, - - // Sequence of bytes representing the namespace table - pub(super) ns_table: NameSpaceTable, - // TODO(X) Revisit caching of frequently used items - // - // TODO type should be `OnceLock` instead of `OnceLock>`. - // We can correct this after `once_cell_try` is stabilized . - // #[derivative(Hash = "ignore")] - // #[derivative(PartialEq = "ignore")] - // #[serde(skip)] - // pub tx_table_len_proof: OnceLock>, -} - -impl Payload { - // TODO dead code even with `pub` because this module is private in lib.rs - #[allow(dead_code)] - pub fn num_namespaces(&self) -> usize { - self.ns_table.len() - } - - // TODO dead code even with `pub` because this module is private in lib.rs - #[allow(dead_code)] - pub fn namespace_iter(&self) -> impl Iterator { - 0..self.ns_table.len() - } - - /// Returns the list of txs for namespace `ns_id`. - pub fn namespace(&self, ns_id: NamespaceId) -> Option> { - let ns_index = self.ns_table.lookup(ns_id)?; - let ns_payload_range = self - .ns_table - .get_payload_range(ns_index, self.raw_payload.len()) - .1; - Some(parse_ns_payload( - self.raw_payload.get(ns_payload_range)?, - ns_id, - )) - } - - // TODO dead code even with `pub` because this module is private in lib.rs - #[allow(dead_code)] - /// Returns the flat bytes for namespace `ns_id`, along with a proof of correctness for those bytes. - /// - /// RPC-friendly proof contains: - /// - the namespace bytes - /// - `vid_common` needed to verify the proof. This data is not accessible to the verifier because it's not part of the block header. - pub fn namespace_with_proof( - &self, - // TODO don't need ns_table any more, it's part of self - ns_table: &NameSpaceTable, - ns_id: NamespaceId, - vid_common: VidCommon, - ) -> Option { - if self.raw_payload.len() != VidSchemeType::get_payload_byte_len(&vid_common) as usize { - return None; // error: vid_common inconsistent with self - } - - let ns_index = if let Some(ns_index) = ns_table.lookup(ns_id) { - ns_index - } else { - return Some(NamespaceProof::NonExistence { ns_id }); - }; - - let ns_payload_range = ns_table - .get_payload_range(ns_index, self.raw_payload.len()) - .1; - - // TODO log output for each `?` - // fix this when we settle on an error handling pattern - Some(NamespaceProof::Existence { - ns_id, - ns_payload_flat: self.raw_payload.get(ns_payload_range.clone())?.into(), - ns_proof: vid_scheme(VidSchemeType::get_num_storage_nodes(&vid_common) as usize) - .payload_proof(&self.raw_payload, ns_payload_range) - .ok()?, - vid_common, - }) - } - - pub fn get_ns_table(&self) -> &NameSpaceTable { - &self.ns_table - } - - pub fn from_txs( - txs: impl IntoIterator as BlockPayload>::Transaction>, - ) -> Result { - let mut namespaces: HashMap = Default::default(); - let mut structured_payload = Self { - raw_payload: vec![], - ns_table: NameSpaceTable::default(), - }; - for tx in txs.into_iter() { - Payload::::update_namespace_with_tx(&mut namespaces, tx); - } - - structured_payload.generate_raw_payload(namespaces)?; - Ok(structured_payload) - } - - fn update_namespace_with_tx( - namespaces: &mut HashMap, - tx: as BlockPayload>::Transaction, - ) { - let tx_bytes_len: TxTableEntry = tx.payload().len().try_into().unwrap(); // TODO (Philippe) error handling - - let namespace = namespaces.entry(tx.namespace()).or_insert(NamespaceInfo { - tx_table: Vec::new(), - tx_bodies: Vec::new(), - tx_bytes_end: TxTableEntry::zero(), - tx_table_len: TxTableEntry::zero(), - }); - - namespace - .tx_bytes_end - .checked_add_mut(tx_bytes_len) - .unwrap(); // TODO (Philippe) error handling - namespace.tx_table.extend(namespace.tx_bytes_end.to_bytes()); - namespace.tx_bodies.extend(tx.payload()); - - namespace - .tx_table_len - .checked_add_mut(TxTableEntry::one()) - .unwrap(); // TODO (Philippe) error handling - } - - fn generate_raw_payload( - &mut self, - namespaces: HashMap, - ) -> Result<(), Error> { - // fill payload and namespace table - let mut payload = vec![]; - - self.ns_table = NameSpaceTable::from_bytes(Vec::from( - TxTableEntry::try_from(namespaces.len()) - .ok() - .context(BlockBuildingSnafu)? - .to_bytes(), - )); - - let mut namespaces_offsets = vec![]; - for (id, namespace) in namespaces { - payload.extend(namespace.tx_table_len.to_bytes()); - payload.extend(namespace.tx_table); - payload.extend(namespace.tx_bodies); - namespaces_offsets.push((id, payload.len())); - } - self.ns_table = NameSpaceTable::from_namespace_offsets(namespaces_offsets).unwrap(); - - self.raw_payload = payload; - Ok(()) - } -} - -impl Display for Payload { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:#?}") - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(bound = "")] // for V -pub enum NamespaceProof { - Existence { - #[serde(with = "base64_bytes")] - ns_payload_flat: Vec, - ns_id: NamespaceId, - ns_proof: LargeRangeProofType, - vid_common: VidCommon, - }, - NonExistence { - ns_id: NamespaceId, - }, -} - -impl NamespaceProof { - /// Verify a [`NamespaceProof`]. - /// - /// All args must be available to the verifier in the block header. - #[allow(dead_code)] // TODO temporary - pub fn verify( - &self, - vid: &VidSchemeType, - commit: &VidCommitment, - ns_table: &NameSpaceTable, - ) -> Option<(Vec, NamespaceId)> { - match self { - NamespaceProof::Existence { - ns_payload_flat, - ns_id, - ns_proof, - vid_common, - } => { - let ns_index = ns_table.lookup(*ns_id)?; - - let (ns_id, ns_payload_range) = ns_table.get_payload_range( - ns_index, - VidSchemeType::get_payload_byte_len(vid_common) as usize, - ); - - // verify self against args - vid.payload_verify( - Statement { - payload_subslice: ns_payload_flat, - range: ns_payload_range, - commit, - common: vid_common, - }, - ns_proof, - ) - .ok()? - .ok()?; - - // verification succeeded, return some data - // we know ns_id is correct because the corresponding ns_payload_range passed verification - Some((parse_ns_payload(ns_payload_flat, ns_id), ns_id)) - } - NamespaceProof::NonExistence { ns_id } => { - if ns_table.lookup(*ns_id).is_some() { - return None; // error: expect not to find ns_id in ns_table - } - Some((Vec::new(), *ns_id)) - } - } - } -} - -pub fn parse_ns_payload(ns_bytes: &[u8], ns_id: NamespaceId) -> Vec { - let num_txs = TxTable::get_tx_table_len(ns_bytes); - (0..TxTable::get_tx_table_len(ns_bytes)) - .map(|tx_idx| TxTable::get_payload_range(ns_bytes, tx_idx, num_txs)) - .map(|tx_range| Transaction::new(ns_id, ns_bytes[tx_range].to_vec())) - .collect() -} - -#[cfg(any(test, feature = "testing"))] -impl hotshot_types::traits::block_contents::TestableBlock - for Payload -{ - fn genesis() -> Self { - BlockPayload::from_transactions([], Arc::new(NodeState::mock())) - .unwrap() - .0 - } - - fn txn_count(&self) -> u64 { - use hotshot_query_service::availability::QueryablePayload; - self.len(&self.ns_table) as u64 - } -} - -#[cfg(test)] -mod test { - use super::NamespaceProof; - use crate::{ - block::{ - entry::{TxTableEntry, TxTableEntryWord}, - payload::{parse_ns_payload, Payload, TableWordTraits}, - queryable, - tables::{test::TxTableTest, NameSpaceTable, Table, TxTable}, - tx_iterator::TxIndex, - }, - transaction::NamespaceId, - Transaction, - }; - use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; - use helpers::*; - use hotshot_query_service::availability::QueryablePayload; - use hotshot_types::{traits::BlockPayload, vid::vid_scheme}; - use jf_primitives::vid::{payload_prover::PayloadProver, VidScheme}; - use rand::RngCore; - use std::{collections::HashMap, marker::PhantomData, ops::Range, sync::Arc}; - - const NUM_STORAGE_NODES: usize = 10; - - #[test] - fn basic_correctness() { - check_basic_correctness::() - } - - fn check_basic_correctness() { - // play with this - let test_cases = [ - // 1 namespace only - vec![vec![5, 8, 8]], // 3 non-empty txs - vec![vec![0, 8, 8]], // 1 empty tx at the beginning - vec![vec![5, 0, 8]], // 1 empty tx in the middle - vec![vec![5, 8, 0]], // 1 empty tx at the end - vec![vec![5]], // 1 nonempty tx - vec![vec![0]], // 1 empty tx - // vec![], // zero txs - vec![vec![1000, 1000, 1000]], // large payload - //multiple namespaces - vec![vec![5, 8, 8], vec![7, 9, 11], vec![10, 5, 8]], // 3 non-empty namespaces - ]; - // TODO(746) future test cases - // vec![vec![], vec![7, 9, 11], vec![10, 5, 8]], // 1 empty namespace at the beginning - // vec![vec![5, 8, 8], vec![], vec![10, 5, 8]], // 1 empty namespace in the middle - // vec![vec![5, 8, 8], vec![7, 9, 11], vec![]], // 1 empty namespace at the end - // vec![vec![0], vec![0, 0]], // 2 non-empty namespaces with all-empty txs - // vec![vec![], vec![]], // 2 empty namespaces - // vec![vec![1000, 1000, 1000], vec![2000, 2000, 2000]], // large payload - - // vec![(0,5), (0,8), (0,8), (1,7), (1,9), (1,11), (2,10), (2,5), (2,8)], // 3 non-empty namespaces, in order - // vec![(14,5), (3,8), (7,8), (7,7), (14,9), (7,11), (3,10), (3,5), (14,8)], // 3 non-empty namespaces, out of order - // vec![(0,0), (1,7), (1,9), (1,11), (2,10), (2,5), (2,8)], // a namespace with 1 empty tx at the beginning - // vec![(0,5), (0,8), (0,8), (1,0), (2,10), (2,5), (2,8)], // a namespace with 1 empty tx in the middle - // vec![(0,0), (1,0)], // 2 namespaces, each with 1 empty tx - - setup_logging(); - setup_backtrace(); - let mut rng = jf_utils::test_rng(); - struct NamespaceInfo { - payload_flat: Vec, - tx_table: Vec, // TODO Philippe => change - #[allow(dead_code)] // TODO temporary - txs: Vec, - } - - let mut vid = vid_scheme(NUM_STORAGE_NODES); - let num_test_cases = test_cases.len(); - for (t, test_case) in test_cases.iter().enumerate() { - // DERIVE A BUNCH OF STUFF FOR THIS TEST CASE - let mut derived_nss = HashMap::new(); - let mut total_num_txs = 0; - for (n, tx_lengths) in test_case.iter().enumerate() { - tracing::info!( - "test block {} of {}, namespace {} of {}, with {} txs", - t + 1, - num_test_cases, - n + 1, - test_case.len(), - tx_lengths.len(), - ); - total_num_txs += tx_lengths.len(); - - // generate this namespace's tx payloads - let entries = entries_from_lengths(tx_lengths); - let tx_payloads_flat = random_bytes(tx_bodies_byte_len(&entries), &mut rng); - let tx_payloads = extract_tx_payloads(&entries, &tx_payloads_flat); - - // enforce well-formed test case - assert_eq!( - tx_payloads_flat, - tx_payloads.iter().flatten().cloned().collect::>(), - "test block {} namespace {} is malformed", - t + 1, - n + 1 - ); - - // derive this namespace's tx table - let tx_table_derived: Vec = tx_payloads - .iter() - .scan(TxTableEntry::zero(), |end, tx| { - end.checked_add_mut(TxTableEntry::try_from(tx.len()).unwrap()) - .unwrap(); - Some(end.clone()) - }) - .collect(); - - // derive this namespace's payload - let ns_payload_flat = { - let mut ns_payload = Vec::new(); - - // write tx table bytes - ns_payload.extend(TxTableEntry::from_usize(tx_table_derived.len()).to_bytes()); - for entry in tx_table_derived.iter() { - ns_payload.extend(entry.to_bytes()); - } - - ns_payload.extend(tx_payloads_flat); - ns_payload - }; - - let new_ns_id = (n as u64).into(); - let already_exists = derived_nss.insert( - new_ns_id, - NamespaceInfo { - payload_flat: ns_payload_flat, - tx_table: tx_table_derived, - txs: tx_payloads - .into_iter() - .map(|p| Transaction::new(new_ns_id, p)) - .collect::>(), - }, - ); - assert!(already_exists.is_none()); - } - assert_eq!(derived_nss.len(), test_case.len()); - - // COMPUTE ACTUAL STUFF AGAINST WHICH TO TEST DERIVED STUFF - let all_txs_iter = derived_nss - .iter() - .flat_map(|(_ns_id, ns)| ns.txs.iter().cloned()); - let (block, actual_ns_table) = - Payload::from_transactions(all_txs_iter, Arc::new(crate::NodeState::mock())) - .unwrap(); - let disperse_data = vid.disperse(&block.raw_payload).unwrap(); - - // TEST ACTUAL STUFF AGAINST DERIVED STUFF - // test total ns length - assert_eq!(block.num_namespaces(), derived_nss.len()); - - // test total tx length - tracing::info!("actual_ns_table {:?}", actual_ns_table); - assert_eq!(block.len(&actual_ns_table), total_num_txs); - // TODO assert the final ns table entry offset == self.payload.len() - - // test namespace table length - let actual_ns_table_len = - TxTableEntry::from_bytes(&actual_ns_table.get_bytes()[..TxTableEntry::byte_len()]) - .unwrap(); - assert_eq!( - actual_ns_table_len, - TxTableEntry::try_from(test_case.len()).unwrap(), - "namespace table length expect {} got {}", - test_case.len(), - actual_ns_table_len - ); - - // test each namespace - // let mut tx_index_offset = 0; - let mut ns_iter = block.namespace_iter(); - let mut block_iter = block.iter(&actual_ns_table); // test iterator correctness - let mut prev_entry = TxTableEntry::zero(); - let mut derived_block_payload = Vec::new(); - for (ns_idx, (ns_id, entry)) in - ns_table_iter::(actual_ns_table.get_bytes()).enumerate() - { - // warning! ns_id may not equal NamespaceId(ns_idx) due to HashMap nondeterminism - - let derived_ns = derived_nss.remove(&ns_id).unwrap(); - - // test ns iterator - let ns_iter_idx = ns_iter.next().unwrap(); - assert_eq!(ns_iter_idx, ns_idx); - - // test ns payload - let actual_ns_payload_range = Range { - start: usize::try_from(prev_entry.clone()).unwrap(), - end: usize::try_from(entry.clone()).unwrap(), - }; - let actual_ns_payload_flat = block - .raw_payload - .get(actual_ns_payload_range.clone()) - .unwrap(); - assert_eq!( - actual_ns_payload_flat, derived_ns.payload_flat, - "namespace {ns_id} incorrect payload bytes", - ); - - // test ns without proof - let ns_txs = block.namespace(ns_id).unwrap(); - assert_eq!( - ns_txs, derived_ns.txs, - "namespace {ns_id} incorrect payload bytes returned from `namespace`", - ); - - // test ns proof - let ns_proof = block - .namespace_with_proof(&actual_ns_table, ns_id, disperse_data.common.clone()) - .unwrap(); - - if let NamespaceProof::Existence { - ref ns_payload_flat, - .. - } = ns_proof - { - assert_eq!( - ns_payload_flat, &derived_ns.payload_flat, - "namespace {ns_id} incorrect payload bytes returned from namespace_with_proof", - ); - } else { - // TODO test for non-existence - panic!("expect NamespaceProof::Existence variant"); - }; - - let (ns_proof_txs, ns_proof_ns_id) = ns_proof - .verify(&vid, &disperse_data.commit, &actual_ns_table) - .unwrap_or_else(|| panic!("namespace {ns_id} proof verification failure")); - assert_eq!(ns_proof_ns_id, ns_id); - assert_eq!(ns_proof_txs, derived_ns.txs); - - // test tx table length - let actual_tx_table_len_bytes = &actual_ns_payload_flat[..TxTableEntry::byte_len()]; - let actual_tx_table_len = - usize::try_from(TxTableEntry::from_bytes(actual_tx_table_len_bytes).unwrap()) - .unwrap(); - assert_eq!( - actual_tx_table_len, - derived_ns.tx_table.len(), - "namespace {ns_id} tx table length expect {} got {}", - derived_ns.tx_table.len(), - actual_tx_table_len - ); - - // test tx table contents - let actual_tx_table_body_bytes = &actual_ns_payload_flat[TxTableEntry::byte_len() - ..(actual_tx_table_len + 1) * TxTableEntry::byte_len()]; - // tracing::info!(ns t"x table bytes {:?}", actual_tx_table_body_bytes); - let actual_tx_table: Vec = actual_tx_table_body_bytes - .chunks(TxTableEntry::byte_len()) - .map(|bytes| TxTableEntry::from_bytes(bytes).unwrap()) - .collect(); - assert_eq!( - actual_tx_table, derived_ns.tx_table, - "namespace {ns_id} incorrect tx table for", - ); - - // testing tx iterator - for tx_idx in 0..derived_ns.tx_table.len() { - let next_tx = block_iter.next().unwrap(); - assert_eq!(ns_idx, next_tx.ns_idx); - assert_eq!(tx_idx, next_tx.tx_idx); - - let idx = TxIndex { ns_idx, tx_idx }; - - // test `transaction()` - let tx = block.transaction(&actual_ns_table, &idx).unwrap(); - assert_eq!(tx, derived_ns.txs[tx_idx]); - - // test `transaction_with_proof()` - let (tx_with_proof, proof) = block - .transaction_with_proof(&actual_ns_table, &idx) - .unwrap(); - assert_eq!(tx, tx_with_proof); - proof - .verify( - &tx_with_proof, - idx, - &vid, - &disperse_data.commit, - &disperse_data.common, - ) - .unwrap() - .unwrap(); - } - - prev_entry = entry; - derived_block_payload.extend(derived_ns.payload_flat.clone()); - } - assert!( - ns_iter.next().is_none(), - "expected ns iterator to be exhausted" - ); - assert!( - block_iter.next().is_none(), - "expected tx iterator to be exhausted" - ); - assert!( - derived_nss.is_empty(), - "some derived namespaces missing from namespace table" - ); - - // test full block payload - // assert_eq!(tx_index_offset, block.len()); - assert_eq!(block.raw_payload, derived_block_payload); - } - } - - #[test] - fn malformed_payloads() { - check_malformed_payloads::(); - //check_malformed_payloads::(); // TODO Philippe this test is failing - } - fn check_malformed_payloads() { - // play with this - let mut rng = jf_utils::test_rng(); - let test_cases = vec![ - // negative-length txs - TestCase::::from_entries(&[30, 10, 20], &mut rng), // 1 negative-length tx - TestCase::from_entries(&[30, 20, 10], &mut rng), // 2 negative-length txs - // truncated payload - TestCase::with_total_len(&[10, 20, 30], 20, &mut rng), // truncated tx payload - TestCase::with_trimmed_body(&[10, 20, 30], 0, &mut rng), // 0-length tx payload - TestCase::with_total_len(&[10, 20, u32::MAX as usize], 1000, &mut rng), // large tx truncated - // negative-length txs AND truncated payload - TestCase::with_total_len(&[30, 20, 10], 20, &mut rng), // negative-len txs, truncated tx payload - TestCase::with_trimmed_body(&[30, 20, 10], 0, &mut rng), // negative-len txs, 0-len tx payload - TestCase::with_total_len(&[10, u32::MAX as usize, 30], 1000, &mut rng), // negative-len tx, large tx truncated - // tx table fits inside payload - TestCase::from_tx_table_len(5, 100, &mut rng), - TestCase::from_tx_table_len(25, 1000, &mut rng), - // tx table too large for payload - TestCase::from_tx_table_len_unchecked(100, 40, &mut rng), - TestCase::from_tx_table_len_unchecked( - 10000, // TODO (Philippe) was TxTableEntry::MAX.try_into().unwrap(), - 100, &mut rng, - ), // huge tx table length - // extra payload bytes - TestCase::with_total_len(&[10, 20, 30], 1000, &mut rng), - TestCase::with_total_len(&[], 1000, &mut rng), // 0 txs - // extremely small payload - TestCase::from_tx_table_len_unchecked(1, 3, &mut rng), // 3-byte payload too small to store tx table len - TestCase::from_tx_table_len_unchecked(1000, 3, &mut rng), // 3-byte payload, large number of txs - TestCase::from_tx_table_len_unchecked(0, 3, &mut rng), // 3-byte payload, 0 txs - TestCase::from_tx_table_len_unchecked(6, 0, &mut rng), // 0-byte payload - ]; - - // TODO(817) more test cases: - // - this will break for extremely large payloads - // - should we hard-code an upper limit so arithmetic never overflows? - - setup_logging(); - setup_backtrace(); - - let mut vid = vid_scheme(NUM_STORAGE_NODES); - let num_test_cases = test_cases.len(); - for (t, test_case) in test_cases.into_iter().enumerate() { - let payload_byte_len = test_case.payload.len(); - tracing::info!( - "test payload {} of {} with {} txs and byte length {}", - t + 1, - num_test_cases, - test_case.num_txs, - payload_byte_len - ); - - // TODO don't initialize Payload with empty namespace table - let block = Payload::from_bytes(&test_case.payload, &NameSpaceTable::default()); - // assert_eq!(block.len(), test_case.num_txs); - assert_eq!(block.raw_payload.len(), payload_byte_len); - - let _disperse_data = vid.disperse(&block.raw_payload).unwrap(); - - // let mut tx_count: ::TransactionIndex = 0; // test iterator correctness - // for index in block.iter() { - // // tracing::info!("tx index {}", index,); - // let (tx, proof) = block.transaction_with_proof(&index).unwrap(); - // proof - // .verify( - // &tx, - // index, - // &vid, - // &disperse_data.commit, - // &disperse_data.common, - // ) - // .unwrap() - // .unwrap(); - // tx_count += 1; - // } - // assert_eq!(test_case.num_txs, usize::try_from(tx_count).unwrap()); - - // test: cannot make a proof for txs outside the tx table - // assert!(block.transaction_with_proof(&tx_count).is_none()); - } - } - - #[test] - fn malicious_tx_inclusion_proof() { - check_malicious_tx_inclusion_proof::(); - check_malicious_tx_inclusion_proof::(); - } - - fn check_malicious_tx_inclusion_proof() { - setup_logging(); - setup_backtrace(); - - let mut rng = jf_utils::test_rng(); - let test_case = TestCase::::from_tx_table_len_unchecked(1, 3, &mut rng); // 3-byte payload too small to store tx table len - - // TODO don't initialize Payload with empty namespace table - let block = Payload::from_bytes(&test_case.payload, &NameSpaceTable::default()); - assert_eq!(block.raw_payload.len(), test_case.payload.len()); - // assert_eq!(block.len(), test_case.num_txs); - - // test: cannot make a proof for such a small block - // assert!(block.transaction_with_proof(&0).is_none()); - - let mut vid = vid_scheme(NUM_STORAGE_NODES); - let disperse_data = vid.disperse(&block.raw_payload).unwrap(); - - // make a fake proof for a nonexistent tx in the small block - let tx = Transaction::new(Default::default(), Vec::new()); - let proof = queryable::gen_tx_proof_for_testing( - 0..block.raw_payload.len(), - TxTableEntry::from_usize(TxTable::get_tx_table_len(&block.raw_payload)), - vid.payload_proof( - &block.raw_payload, - 0..std::cmp::min(TxTableEntry::byte_len(), block.raw_payload.len()), - ) - .unwrap(), - vid.payload_proof(&block.raw_payload, 0..3).unwrap(), - ); - - // test: fake proof should get rejected - // TODO should return Some(Err()) instead of None - assert!(proof - .verify( - &tx, - TxIndex { - ns_idx: 0, - tx_idx: 0 - }, - &vid, - &disperse_data.commit, - &disperse_data.common - ) - .is_none()); - } - - #[test] - fn arbitrary_payloads() { - check_arbitrary_ns_table::(); - check_arbitrary_tx_table::(); - } - - fn check_arbitrary_ns_table() { - setup_logging(); - setup_backtrace(); - let mut rng = jf_utils::test_rng(); - let entry_len = TxTableEntry::byte_len(); - let mut vid = vid_scheme(NUM_STORAGE_NODES); - - // test 1 - let mut ns1 = vec![0; 100]; - rng.fill_bytes(&mut ns1); - write_usize(&mut ns1, 0, 13); - - // test 2 - let mut ns2 = vec![0; 100]; - rng.fill_bytes(&mut ns2); - write_usize(&mut ns2, 0, 12); - - // test 3 - let mut ns3 = vec![0; 100]; - rng.fill_bytes(&mut ns3); - write_usize(&mut ns3, 0, 12); - write_usize(&mut ns3, 2 * entry_len, 26); - - // test 4 - let namespace_offsets = vec![ - (NamespaceId::from(0), 100), - (NamespaceId::from(1), 200), - (NamespaceId::from(2), 300), - (NamespaceId::from(3), 50), - (NamespaceId::from(4), 150), - ]; - let ns4 = NameSpaceTable::::from_namespace_offsets(namespace_offsets) - .unwrap() - .get_bytes() - .to_vec(); - - let test_cases = vec![ - // test 0: arbitrary random bytes - vec![random_bytes(100, &mut rng), random_bytes(2000, &mut rng)], - vec![vec![], random_bytes(100, &mut rng)], - vec![random_bytes(100, &mut rng), vec![]], - vec![vec![0u8, 0u8, 3u8], random_bytes(100, &mut rng)], - // test 1: ns-table suggests 13 entries but ns-table length is only 100 bytes (max 12 namespaces) - vec![ns1, random_bytes(130, &mut rng)], - // test 2: ns-table suggests 12 entries but payload is 47 bytes => 11 empty namespaces - // vec![ns2, random_bytes(47, &mut rng)], - - // test 3: first entry in ns-table points to offset (26 * entry_len) but payload is only 100 bytes (max 25 namespaces) - vec![ns3, random_bytes(100, &mut rng)], - // test 4: overlapping namespaces is allowed but results in a zero-length namespace - vec![ns4, random_bytes(300, &mut rng)], - // test 5: more than one namespace with the same namespace id - ]; - - for test_case in test_cases.into_iter() { - let actual_ns_table_bytes = &test_case[0]; - let actual_payload_bytes = &test_case[1]; - - let block = Payload::from_bytes( - actual_payload_bytes, - &NameSpaceTable::from_bytes(actual_ns_table_bytes.to_vec()), - ); - let disperse_data = vid.disperse(&block.raw_payload).unwrap(); - - let ns_table = block.get_ns_table(); - let ns_table_len = ns_table.len(); - - let actual_ns_table_len = { - let left = read_usize(actual_ns_table_bytes, 0); - let right = actual_ns_table_bytes - .len() - .saturating_sub(TxTableEntry::byte_len()) - / (2 * TxTableEntry::byte_len()); - std::cmp::min(left, right) - }; - - assert_eq!( - ns_table_len, actual_ns_table_len, - "deduced ns table len is {} but actual ns table len is {}", - ns_table_len, actual_ns_table_len - ); - - let mut last_offset = 0; - for ns_idx in 0..ns_table_len { - let (ns_id, ns_range) = ns_table.get_payload_range(ns_idx, block.raw_payload.len()); - // test ns range - let start = ns_range.start; - let end = ns_range.end; - assert!(start <= end, "ensure valid range for namespace",); - assert!( - end <= block.raw_payload.len(), - "deduced range of ns_idx: {} is ending at: {} but payload length is only: {}", - ns_idx, - end, - actual_ns_table_bytes.len(), - ); - - // test ns proof - let ns_proof_option = block.namespace_with_proof( - block.get_ns_table(), - ns_id, - disperse_data.common.clone(), - ); - if let Some(ns_proof) = ns_proof_option { - if let NamespaceProof::Existence { - ref ns_payload_flat, - .. - } = ns_proof - { - assert_eq!( - ns_payload_flat, &block.raw_payload[ns_range.clone()], - "namespace {} incorrect payload bytes returned from namespace_with_proof", - ns_id, - ); - } else { - panic!("expect NamespaceProof::Existence variant"); - }; - } else { - assert!(ns_range.is_empty()); - } - - // test overlapping namespaces - if ns_range.end < last_offset { - assert!(ns_range.is_empty(), "identified overlapping namespaces but the resulting namespace range is not empty"); - } - last_offset = ns_range.end; - } - } - } - - fn check_arbitrary_tx_table() { - setup_logging(); - setup_backtrace(); - let mut rng = jf_utils::test_rng(); - let entry_len = TxTableEntry::byte_len(); - - // test 1 - let namespace_offsets = vec![ - (NamespaceId::from(0), 100), - (NamespaceId::from(1), 200), - (NamespaceId::from(2), 300), - ]; - let ns1 = NameSpaceTable::::from_namespace_offsets(namespace_offsets) - .unwrap() - .get_bytes() - .to_vec(); - let mut payload1 = vec![0; 300]; - rng.fill_bytes(&mut payload1); - write_usize(&mut payload1, 0, 25); - - // test 2 - let ns2 = ns1.clone(); - let mut payload2 = vec![0; 300]; - rng.fill_bytes(&mut payload2); - write_usize(&mut payload2, 0, 5); - write_usize(&mut payload2, entry_len, 101); - - // test 3 - let ns3 = ns1.clone(); - let mut payload3 = vec![0; 300]; - rng.fill_bytes(&mut payload3); - write_usize(&mut payload3, 200, 5); - write_usize(&mut payload3, 200 + entry_len, 6); - write_usize(&mut payload3, 200 + (2 * entry_len), 6); - write_usize(&mut payload3, 200 + (3 * entry_len), 101); - - // test 4 - let namespace_offsets = vec![ - (NamespaceId::from(0), 1000), - (NamespaceId::from(1), 1300), - (NamespaceId::from(2), 2300), - ]; - let ns4 = NameSpaceTable::::from_namespace_offsets(namespace_offsets) - .unwrap() - .get_bytes() - .to_vec(); - let mut payload4 = vec![0; 2300]; - rng.fill_bytes(&mut payload4); - write_usize(&mut payload4, 1000, 5); - write_usize(&mut payload4, 1000 + entry_len, 100); - write_usize(&mut payload4, 1000 + (2 * entry_len), 200); - write_usize(&mut payload4, 1000 + (3 * entry_len), 300); - write_usize(&mut payload4, 1000 + (4 * entry_len), 50); - write_usize(&mut payload4, 1000 + (5 * entry_len), 150); - - let test_cases = vec![ - // test 1: tx-table suggests 25 entries but ns length is only 100 bytes (max 24 txs) - vec![ns1, payload1], - // test 2: first entry in tx-table points to offset 101 but ns is only 100 bytes - vec![ns2, payload2], - // test 3: first two namespaces are random bytes. - // the third namespace has 5 txs - vec![ns3, payload3], - // test 4: 3 namespaces where first and last are random bytes. - // the middle namespace has overlapping transaction payloads - vec![ns4, payload4], - ]; - - for test_case in test_cases.into_iter() { - let actual_ns_table_bytes = &test_case[0]; - let actual_payload_bytes = &test_case[1]; - - let block = Payload::from_bytes( - actual_payload_bytes, - &NameSpaceTable::from_bytes(actual_ns_table_bytes.to_vec()), - ); - let ns_table = block.get_ns_table(); - let mut total_tx_num = 0; - let mut tx_iter = block.iter(ns_table); - for ns_idx in 0..ns_table.len() { - let (ns_id, ns_range) = ns_table.get_payload_range(ns_idx, block.raw_payload.len()); - let ns_bytes = &block.raw_payload[ns_range.clone()]; - - // ns cannot hold more than max num of txs - let tx_table_len = TxTable::get_tx_table_len(ns_bytes); - let max_tx_table_len = ns_bytes.len().saturating_sub(TxTableEntry::byte_len()) - / TxTableEntry::byte_len(); - assert!( - tx_table_len <= max_tx_table_len, - "derived tx table len is {} but actual ns has room only for {} txs", - tx_table_len, - max_tx_table_len - ); - - let txs = parse_ns_payload(ns_bytes, ns_id); - total_tx_num += txs.len(); - - let actual_tx_table_len = read_usize(ns_bytes, 0); - if max_tx_table_len < actual_tx_table_len { - assert!(txs.iter().all(|tx| tx.payload().is_empty()), - "advertised tx-table length cannot possibly fit in namespace; all txs should be empty"); - } - - let tx_payloads_offset = (tx_table_len + 1) * TxTableEntry::byte_len(); - let mut last_offset = tx_payloads_offset; - let mut tx_offset_bytes = vec![0u8; TxTableEntry::byte_len()]; - - for (tx_idx, tx) in txs.iter().enumerate() { - assert!(tx_iter.next().is_some()); - - let tx_range = TxTable::get_payload_range(ns_bytes, tx_idx, tx_table_len); - // read tx end offset directly from raw payload bytes - tx_offset_bytes[..TxTableEntry::byte_len()].copy_from_slice( - &actual_payload_bytes[ns_range.start - + (tx_idx + 1) * TxTableEntry::byte_len() - ..(ns_range.start + (tx_idx + 2) * TxTableEntry::byte_len())], - ); - let tx_offset = usize::try_from( - TxTableEntry::from_bytes(&tx_offset_bytes).unwrap_or(TxTableEntry::zero()), - ) - .unwrap_or(0); - - let mut malformed = false; - let actual_tx_offset = tx_payloads_offset + tx_offset; - - // check derived tx byte range - if actual_tx_offset > ns_bytes.len() { - assert_eq!( - tx_range.end, - ns_bytes.len(), - "tx offset should be clamped at the end of namespace" - ); - if last_offset > ns_bytes.len() { - assert_eq!( - tx.payload().len(), - 0, - "tx payload should be empty if start and end are both clamped" - ); - } - malformed = true; - } - - // check overlapping tx payloads - if actual_tx_offset < last_offset { - assert_eq!( - tx.payload().len(), - 0, - "identified overlapping tx payloads; negative length tx is empty" - ); - malformed = true; - } - - // derive tx-length if tx is not malformed - if !malformed { - assert_eq!( - tx.payload().len(), - actual_tx_offset - last_offset, - "tx payload is derived to be {} but should be {}", - tx.payload().len(), - actual_tx_offset - last_offset - ); - } - last_offset = actual_tx_offset; - } - } - assert_eq!( - block.len(&block.ns_table), - total_tx_num, - "block has {} txs but number of total tx from all namespaces is {}", - block.len(&block.ns_table), - total_tx_num - ) - } - } - - struct TestCase { - payload: Vec, - num_txs: usize, - phantomdata: PhantomData, - } - impl TestCase { - /// Return a well-formed random block whose tx table is derived from `lengths`. - #[allow(dead_code)] - fn from_lengths(lengths: &[usize], rng: &mut R) -> Self { - Self::from_entries(&entries_from_lengths(lengths), rng) - } - - /// Return a random block whose tx table is derived from `entries`. - /// - /// If `entries` is well-formed then the result is well-formed. - fn from_entries(entries: &[usize], rng: &mut R) -> Self { - let tx_table = TxTableTest::::from_entries(entries); - Self { - payload: [ - tx_table.get_payload(), - random_bytes(tx_bodies_byte_len(entries), rng), - ] - .concat(), - num_txs: entries.len(), - phantomdata: Default::default(), - } - } - - /// Like `from_entries` except the tx bodies byte length is `body_len`. - /// - /// Panics if `body_len` would not actually decrease the block size. - fn with_trimmed_body(entries: &[usize], body_len: usize, rng: &mut R) -> Self { - assert!( - body_len < tx_bodies_byte_len(entries), - "body_len too large to trim the body" - ); - let tx_table = TxTableTest::::from_entries(entries); - Self { - payload: [tx_table.get_payload(), random_bytes(body_len, rng)].concat(), - num_txs: entries.len(), - phantomdata: Default::default(), - } - } - - /// Like `from_entries` except the byte length of the block is `block_byte_len`. - /// - /// Panics if `block_byte_len` would truncate the tx table. - /// If you want to truncate the tx table then use `with_total_len_unchecked`. - /// - /// If `block_byte_len` would increase block size then new space is filled with random bytes. - fn with_total_len( - entries: &[usize], - block_byte_len: usize, - rng: &mut R, - ) -> Self { - assert!( - tx_table_byte_len::(entries) <= block_byte_len, - "tx table size {} for entries {:?} exceeds block_byte_len {}", - tx_table_byte_len::(entries), - entries, - block_byte_len - ); - Self::with_total_len_unchecked(entries, block_byte_len, rng) - } - - /// Like `with_total_len` except `block_byte_len` may truncate the tx table. - fn with_total_len_unchecked( - entries: &[usize], - block_byte_len: usize, - rng: &mut R, - ) -> Self { - let tx_table = TxTableTest::::from_entries(entries); - let mut payload = tx_table.get_payload(); - let num_txs = if block_byte_len > payload.len() { - payload.extend(random_bytes(block_byte_len - payload.len(), rng)); - entries.len() - } else { - payload.truncate(block_byte_len); - (block_byte_len / TxTableTest::::byte_len()).saturating_sub(1) - }; - Self { - payload, - num_txs, - phantomdata: Default::default(), - } - } - - /// Return a random block whose tx table indicates `tx_table_len` txs and whose total byte length is `block_byte_len`. - /// - /// Every byte of the block is random except the tx table header. - /// - /// Panics if `txs_byte_len` would truncate the tx table. - /// If you want to truncate the tx table then use `with_total_len_unchecked`. - fn from_tx_table_len( - tx_table_len: usize, - block_byte_len: usize, - rng: &mut R, - ) -> Self { - let tx_table_byte_len = (tx_table_len + 1) * TxTableTest::::byte_len(); - assert!( - tx_table_byte_len <= block_byte_len, - "tx table size {} exceeds block size {}", - tx_table_byte_len, - block_byte_len - ); - Self::from_tx_table_len_unchecked(tx_table_len, block_byte_len, rng) - } - - /// Like `from_tx_table_len` except `block_byte_len` may truncate the tx table. - fn from_tx_table_len_unchecked( - tx_table_len: usize, - block_byte_len: usize, - rng: &mut R, - ) -> Self { - // accommodate extremely small block payload - let header_byte_len = - std::cmp::min(TxTableTest::::byte_len(), block_byte_len); - let mut payload = vec![0; block_byte_len]; - rng.fill_bytes(&mut payload); - payload[..header_byte_len].copy_from_slice( - &TxTableEntry::from_usize(tx_table_len).to_bytes()[..header_byte_len], // TODO (Philippe) remove - ); - Self { - payload, - num_txs: std::cmp::min( - tx_table_len, - (block_byte_len / TxTableTest::::byte_len()).saturating_sub(1), - ), - phantomdata: Default::default(), - } - } - } - - mod helpers { - use crate::block::entry::TxTableEntry; - use crate::block::payload::TableWordTraits; - use crate::block::tables::{test::TxTableTest, NameSpaceTable, Table}; - use crate::NamespaceId; - use rand::RngCore; - - pub fn tx_table_byte_len(entries: &[usize]) -> usize { - (entries.len() + 1) * TxTableTest::::byte_len() - } - - pub fn entries_from_lengths(lengths: &[usize]) -> Vec { - lengths - .iter() - .scan(0, |sum, &len| { - *sum += len; - Some(*sum) - }) - .collect() - } - - #[test] - fn tx_table_helpers() { - assert_eq!(vec![10, 20, 30], entries_from_lengths(&[10, 10, 10])); - } - - pub fn tx_bodies_byte_len(entries: &[usize]) -> usize { - // largest entry in the tx table dictates size of tx payloads - *entries.iter().max().unwrap_or(&0) - } - - pub fn write_usize(bytes: &mut [u8], pos: usize, val: usize) { - let end = std::cmp::min(pos + TxTableEntry::byte_len(), bytes.len()); - let start = std::cmp::min(pos, end); - let range = start..end; - bytes[range.clone()] - .copy_from_slice(&TxTableEntry::from_usize(val).to_bytes()[..range.len()]); - } - - pub fn read_usize(bytes: &[u8], pos: usize) -> usize { - let end = std::cmp::min(pos + TxTableEntry::byte_len(), bytes.len()); - let start = std::cmp::min(pos, end); - let range = start..end; - let mut entry_bytes = [0u8; TxTableEntry::byte_len()]; - entry_bytes[..range.len()].copy_from_slice(&bytes[start..end]); - TxTableEntry::from_bytes_array(entry_bytes) - .try_into() - .unwrap() - } - - pub fn random_bytes(len: usize, rng: &mut R) -> Vec { - let mut result = vec![0; len]; - rng.fill_bytes(&mut result); - result - } - - pub fn extract_tx_payloads(entries: &[usize], tx_payloads_flat: &[u8]) -> Vec> { - let mut result = Vec::with_capacity(entries.len()); - let mut start = 0; - for end in entries { - let end = std::cmp::min(*end, tx_payloads_flat.len()); - let tx_payload = if start >= end { - Vec::new() - } else { - tx_payloads_flat[start..end].to_vec() - }; - start = end; - result.push(tx_payload); - } - assert_eq!( - result.len(), - entries.len(), - "bug in test code: expect to extract {} txs but got {}", - entries.len(), - result.len() - ); - result - } - - pub fn ns_table_iter( - ns_table_bytes: &[u8], - ) -> impl Iterator + '_ { - ns_table_bytes[NameSpaceTable::::byte_len()..] // first few bytes is the table length, skip that - .chunks(2 * TxTableEntry::byte_len()) - .map(|bytes| { - // read (namespace id, entry) from the namespace table - let ns_id = NamespaceId::try_from( - TxTableEntry::from_bytes(&bytes[..TxTableEntry::byte_len()]).unwrap(), - ) - .unwrap(); - let entry = - TxTableEntry::from_bytes(&bytes[TxTableEntry::byte_len()..]).unwrap(); - (ns_id, entry) - }) - } - } -} diff --git a/sequencer/src/block/queryable.rs b/sequencer/src/block/queryable.rs deleted file mode 100644 index f1d50877a..000000000 --- a/sequencer/src/block/queryable.rs +++ /dev/null @@ -1,334 +0,0 @@ -use crate::block::entry::TxTableEntryWord; -use crate::block::payload::Payload; -use crate::block::tables::TxTable; -use hotshot_query_service::availability::QueryablePayload; -use hotshot_types::vid::{vid_scheme, SmallRangeProofType}; -use jf_primitives::vid::payload_prover::{PayloadProver, Statement}; -use serde::{Deserialize, Serialize}; -use std::ops::Range; - -use crate::Transaction; - -use super::{ - entry::TxTableEntry, - tx_iterator::{TxIndex, TxIterator}, -}; - -// TODO don't hard-code TxTableEntryWord generic param -impl QueryablePayload for Payload { - type TransactionIndex = TxIndex; - type Iter<'a> = TxIterator<'a, TxTableEntryWord>; - type InclusionProof = TxInclusionProof; - - fn len(&self, ns_table: &Self::Metadata) -> usize { - (0..ns_table.len()) - .map(|ns_idx| ns_table.get_payload_range(ns_idx, self.raw_payload.len()).1) - .map(|ns_range| TxTable::get_tx_table_len(&self.raw_payload[ns_range])) - .sum() - } - - fn iter<'a>(&'a self, ns_table: &'a Self::Metadata) -> Self::Iter<'a> { - TxIterator::new(ns_table, self) - } - - fn transaction( - &self, - meta: &Self::Metadata, - index: &Self::TransactionIndex, - ) -> Option { - let (ns_idx, tx_idx) = (index.ns_idx, index.tx_idx); - if ns_idx >= meta.len() { - return None; // error: index out of bounds - } - let (ns_id, ns_range) = meta.get_payload_range(ns_idx, self.raw_payload.len()); - - let tx_table_len = TxTable::get_tx_table_len(&self.raw_payload[ns_range.clone()]); - if tx_idx >= tx_table_len { - return None; // error: index out of bounds - } - let ns_payload = &self.raw_payload[ns_range.clone()]; - - let tx_within_ns = TxTable::get_payload_range(ns_payload, tx_idx, tx_table_len); - let (start, end) = (tx_within_ns.start, tx_within_ns.end); - let ns_start = ns_range.start; - let tx_payload_range = start.saturating_add(ns_start)..end.saturating_add(ns_start); - - let tx_payload = self.raw_payload.get(tx_payload_range)?.to_vec(); - - Some(Transaction::new(ns_id, tx_payload)) - } - - fn transaction_with_proof( - &self, - meta: &Self::Metadata, - index: &Self::TransactionIndex, - ) -> Option<(Self::Transaction, Self::InclusionProof)> { - let (ns_idx, tx_idx) = (index.ns_idx, index.tx_idx); - if ns_idx >= meta.len() { - return None; // error: index out of bounds - } - let (ns_id, ns_range) = meta.get_payload_range(ns_idx, self.raw_payload.len()); - let ns_start_offset = ns_range.start; - - let tx_table_len = TxTable::get_tx_table_len(&self.raw_payload[ns_range.clone()]); - if tx_idx >= tx_table_len { - return None; // error: index out of bounds - } - - let tx_payloads_offset = tx_table_len - .checked_add(1)? - .checked_mul(TxTableEntry::byte_len())? - .checked_add(ns_start_offset)?; - - // TODO temporary VID construction. We need to get the number of storage nodes from the VID - // common data. May need the query service to pass common into this function along with - // metadata. - let vid = vid_scheme(10); - - // Read the tx payload range from the tx table into `tx_table_range_[start|end]` and compute a proof that this range is correct. - // - // This correctness proof requires a range of its own, which we read into `tx_table_range_proof_[start|end]`. - // - // Edge case--the first transaction: tx payload range `start` is implicitly 0 and we do not include this item in the correctness proof. - // - // TODO why isn't cargo fmt wrapping these comments? - - // start - let (tx_table_range_proof_start, tx_table_range_start) = if tx_idx == 0 { - (TxTableEntry::byte_len().checked_add(ns_start_offset)?, None) - } else { - let range_proof_start = tx_idx - .checked_mul(TxTableEntry::byte_len())? - .checked_add(ns_start_offset)?; - ( - range_proof_start, - Some(TxTableEntry::from_bytes(self.raw_payload.get( - range_proof_start..range_proof_start.checked_add(TxTableEntry::byte_len())?, - )?)?), - ) - }; - - // end - let tx_table_range_proof_end = tx_idx - .checked_add(2)? - .checked_mul(TxTableEntry::byte_len())? - .checked_add(ns_start_offset)?; - - let tx_table_range_end = TxTableEntry::from_bytes(self.raw_payload.get( - tx_table_range_proof_end.checked_sub(TxTableEntry::byte_len())? - ..tx_table_range_proof_end, - )?)?; - - let tx_payload_range = { - let start = - usize::try_from(tx_table_range_start.clone().unwrap_or(TxTableEntry::zero())) - .ok()? - .checked_add(tx_payloads_offset)?; - let end = usize::try_from(tx_table_range_end.clone()) - .ok()? - .checked_add(tx_payloads_offset)?; - let end = std::cmp::min(end, ns_range.end); - let start = std::cmp::min(start, end); - start..end - }; - - // correctness proof for the tx payload range - let tx_table_range_proof = vid - .payload_proof( - &self.raw_payload, - tx_table_range_proof_start..tx_table_range_proof_end, - ) - .ok()?; - let tx_table_len_range = ns_range.start - ..std::cmp::min( - ns_range.end, - ns_range.start.checked_add(TxTableEntry::byte_len())?, - ); - Some(( - // TODO don't copy the tx bytes into the return value - // https://github.com/EspressoSystems/hotshot-query-service/issues/267 - Transaction::new( - ns_id, - self.raw_payload.get(tx_payload_range.clone())?.to_vec(), - ), - TxInclusionProof { - ns_range: ns_range.clone(), - tx_table_len: TxTableEntry::from_usize(tx_table_len), - tx_table_len_proof: vid - .payload_proof(&self.raw_payload, tx_table_len_range) - .unwrap(), - tx_table_range_start, - tx_table_range_end, - tx_table_range_proof, - tx_payload_proof: if tx_payload_range.is_empty() { - None - } else { - vid.payload_proof(&self.raw_payload, tx_payload_range).ok() - }, - }, - )) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct TxInclusionProof { - ns_range: Range, - tx_table_len: TxTableEntry, - tx_table_len_proof: SmallRangeProofType, - - tx_table_range_start: Option, // `None` for the 0th tx - tx_table_range_end: TxTableEntry, - tx_table_range_proof: SmallRangeProofType, - - tx_payload_proof: Option, // `None` if the tx has zero length -} - -impl TxInclusionProof { - // TODO currently broken, fix in https://github.com/EspressoSystems/espresso-sequencer/issues/1010 - // - // - We need to decide where to store VID params. - // - Returns `None` if an error occurred. - // - Use of `Result<(),()>` pattern to enable use of `?` for concise abort-on-failure. - #[allow(dead_code)] // TODO temporary - #[allow(clippy::too_many_arguments)] - pub fn verify( - &self, - tx: &Transaction, - tx_index: TxIndex, - vid: &V, - vid_commit: &V::Commit, - vid_common: &V::Common, - ) -> Option> - where - V: PayloadProver, - { - V::is_consistent(vid_commit, vid_common).ok()?; - - // Verify proof for tx payload. - // Proof is `None` if and only if tx has zero length. - let tx_payloads_offset = usize::try_from(self.tx_table_len.clone()) - .ok()? - .checked_add(1)? - .checked_mul(TxTableEntry::byte_len())? - .checked_add(self.ns_range.start)?; - let tx_payload_range = { - let start = usize::try_from( - self.tx_table_range_start - .clone() - .unwrap_or(TxTableEntry::zero()), - ) - .ok()? - .checked_add(tx_payloads_offset)?; - let end = usize::try_from(self.tx_table_range_end.clone()) - .ok()? - .checked_add(tx_payloads_offset)?; - let end = std::cmp::min(end, self.ns_range.end); - let start = std::cmp::min(start, end); - start..end - }; - match &self.tx_payload_proof { - Some(tx_payload_proof) => { - if vid - .payload_verify( - Statement { - payload_subslice: tx.payload(), - range: tx_payload_range, - commit: vid_commit, - common: vid_common, - }, - tx_payload_proof, - ) - .ok()? - .is_err() - { - return Some(Err(())); // TODO it would be nice to use ? here... - } - } - None => { - if !tx.payload().is_empty() || !tx_payload_range.is_empty() { - return None; // error: nonempty payload but no proof - } - } - }; - - // Verify proof for tx table len. - if vid - .payload_verify( - Statement { - payload_subslice: &self.tx_table_len.to_bytes(), - range: self.ns_range.start - ..self.ns_range.start.checked_add(TxTableEntry::byte_len())?, - commit: vid_commit, - common: vid_common, - }, - &self.tx_table_len_proof, - ) - .ok()? - .is_err() - { - return Some(Err(())); - } - - // Verify proof for tx table entries. - // Start index missing for the 0th tx - let index: usize = tx_index.tx_idx; - let mut tx_table_range_bytes = - Vec::with_capacity(2usize.checked_mul(TxTableEntry::byte_len())?); - let start = if let Some(tx_table_range_start) = &self.tx_table_range_start { - if index == 0 { - return None; // error: first tx should have empty start index - } - tx_table_range_bytes.extend(tx_table_range_start.to_bytes()); - index - .checked_mul(TxTableEntry::byte_len())? - .checked_add(self.ns_range.start)? - } else { - if index != 0 { - return None; // error: only the first tx should have empty start index - } - TxTableEntry::byte_len().checked_add(self.ns_range.start)? - }; - tx_table_range_bytes.extend(self.tx_table_range_end.to_bytes()); - let range = start - ..index - .checked_add(2)? - .checked_mul(TxTableEntry::byte_len())? - .checked_add(self.ns_range.start)?; - - if vid - .payload_verify( - Statement { - payload_subslice: &tx_table_range_bytes, - range, - commit: vid_commit, - common: vid_common, - }, - &self.tx_table_range_proof, - ) - .ok()? - .is_err() - { - return Some(Err(())); - } - - Some(Ok(())) - } -} - -#[cfg(test)] -pub(crate) fn gen_tx_proof_for_testing( - ns_range: Range, - tx_table_len: TxTableEntry, - tx_table_len_proof: SmallRangeProofType, - payload_proof: SmallRangeProofType, -) -> TxInclusionProof { - TxInclusionProof { - ns_range, - tx_table_len, - tx_table_len_proof, - tx_table_range_start: None, - tx_table_range_end: TxTableEntry::from_usize(1), - tx_table_range_proof: payload_proof, - tx_payload_proof: None, - } -} diff --git a/sequencer/src/block/tables.rs b/sequencer/src/block/tables.rs deleted file mode 100644 index bb00150e7..000000000 --- a/sequencer/src/block/tables.rs +++ /dev/null @@ -1,305 +0,0 @@ -use crate::block::entry::TxTableEntry; -use crate::block::payload::TableWordTraits; -use crate::{BlockBuildingSnafu, Error, NamespaceId}; -use derivative::Derivative; -use hotshot_types::traits::EncodeBytes; -use serde::{Deserialize, Serialize}; -use snafu::OptionExt; -use std::marker::PhantomData; -use std::mem::size_of; -use std::ops::Range; -use std::sync::Arc; - -pub trait Table { - // Read TxTableEntry::byte_len() bytes from `table_bytes` starting at `offset`. - // if `table_bytes` has too few bytes at this `offset` then pad with zero. - // Parse these bytes into a `TxTableEntry` and return. - // Returns raw bytes, no checking for large values - fn get_table_len(&self, offset: usize) -> TxTableEntry; - - fn byte_len() -> usize { - size_of::() - } -} - -impl Table for NameSpaceTable { - // TODO (Philippe) avoid code duplication with similar function in TxTable? - fn get_table_len(&self, offset: usize) -> TxTableEntry { - let end = std::cmp::min( - offset.saturating_add(TxTableEntry::byte_len()), - self.bytes.len(), - ); - let start = std::cmp::min(offset, end); - let tx_table_len_range = start..end; - let mut entry_bytes = [0u8; TxTableEntry::byte_len()]; - entry_bytes[..tx_table_len_range.len()].copy_from_slice(&self.bytes[tx_table_len_range]); - TxTableEntry::from_bytes_array(entry_bytes) - } -} - -#[derive(Clone, Debug, Derivative, Deserialize, Eq, Serialize, Default)] -#[derivative(Hash, PartialEq)] -pub struct NameSpaceTable { - #[serde(with = "base64_bytes")] - pub(super) bytes: Vec, - #[serde(skip)] - pub(super) phantom: PhantomData, -} - -impl EncodeBytes for NameSpaceTable { - fn encode(&self) -> std::sync::Arc<[u8]> { - Arc::from(self.bytes.clone()) - } -} - -impl NameSpaceTable { - pub fn from_bytes(bytes: impl Into>) -> Self { - Self { - bytes: bytes.into(), - phantom: Default::default(), - } - } - - pub fn from_namespace_offsets( - namespace_offsets: Vec<(NamespaceId, usize)>, - ) -> Result { - let mut ns_table = NameSpaceTable::from_bytes( - TxTableEntry::try_from(namespace_offsets.len()) - .ok() - .context(BlockBuildingSnafu)? - .to_bytes(), - ); - for (id, offset) in namespace_offsets { - ns_table.add_new_entry_ns_id(id)?; - ns_table.add_new_entry_payload_len(offset)?; - } - Ok(ns_table) - } - - pub fn get_bytes(&self) -> &[u8] { - &self.bytes - } - - /// Find `ns_id` and return its index into this namespace table. - /// - /// TODO return Result or Option? Want to avoid catch-all Error type :( - pub fn lookup(&self, ns_id: NamespaceId) -> Option { - (0..self.len()).find(|&ns_index| ns_id == self.get_table_entry(ns_index).0) - } - - fn add_new_entry_ns_id(&mut self, id: NamespaceId) -> Result<(), Error> { - self.bytes.extend( - TxTableEntry::try_from(id) - .ok() - .context(BlockBuildingSnafu)? - .to_bytes(), - ); - Ok(()) - } - - fn add_new_entry_payload_len(&mut self, l: usize) -> Result<(), Error> { - self.bytes.extend( - TxTableEntry::try_from(l) - .ok() - .context(BlockBuildingSnafu)? - .to_bytes(), - ); - Ok(()) - } - - // Parse the table length from the beginning of the namespace table. - // Returned value is guaranteed to be no larger than the number of ns table entries that could possibly fit into `ns_table_bytes`. - pub fn len(&self) -> usize { - let left = self.get_table_len(0).try_into().unwrap_or(0); - let right = self.bytes.len().saturating_sub(TxTableEntry::byte_len()) - / (2 * TxTableEntry::byte_len()); - std::cmp::min(left, right) - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - // returns (ns_id, ns_offset) - // ns_offset is not checked, could be anything - pub fn get_table_entry(&self, ns_index: usize) -> (NamespaceId, usize) { - // get the range for ns_id bytes in ns table - // ensure `range` is within range for ns_table_bytes - let start = std::cmp::min( - ns_index - .saturating_mul(2) - .saturating_add(1) - .saturating_mul(TxTableEntry::byte_len()), - self.bytes.len(), - ); - let end = std::cmp::min( - start.saturating_add(TxTableEntry::byte_len()), - self.bytes.len(), - ); - let ns_id_range = start..end; - - // parse ns_id bytes from ns table - // any failure -> NamespaceId::default() - let mut ns_id_bytes = [0u8; TxTableEntry::byte_len()]; - ns_id_bytes[..ns_id_range.len()].copy_from_slice(&self.bytes[ns_id_range]); - let ns_id = NamespaceId::try_from( - TxTableEntry::from_bytes(&ns_id_bytes).unwrap_or(TxTableEntry::zero()), - ) - .unwrap_or_default(); - - // get the range for ns_offset bytes in ns table - // ensure `range` is within range for ns_table_bytes - // TODO refactor range checking code - let start = end; - let end = std::cmp::min( - start.saturating_add(TxTableEntry::byte_len()), - self.bytes.len(), - ); - let ns_offset_range = start..end; - - // parse ns_offset bytes from ns table - // any failure -> 0 offset (?) - // TODO refactor parsing code? - let mut ns_offset_bytes = [0u8; TxTableEntry::byte_len()]; - ns_offset_bytes[..ns_offset_range.len()].copy_from_slice(&self.bytes[ns_offset_range]); - let ns_offset = usize::try_from( - TxTableEntry::from_bytes(&ns_offset_bytes).unwrap_or(TxTableEntry::zero()), - ) - .unwrap_or(0); - - (ns_id, ns_offset) - } - - /// Like `tx_payload_range` except for namespaces. - /// Returns the ns id and the ns byte range in the block payload bytes. - /// - /// Ensures that the returned range is valid: `start <= end <= block_payload_byte_len`. - pub fn get_payload_range( - &self, - ns_index: usize, - block_payload_byte_len: usize, - ) -> (NamespaceId, Range) { - let (ns_id, offset) = self.get_table_entry(ns_index); - let end = std::cmp::min(offset, block_payload_byte_len); - let start = if ns_index == 0 { - 0 - } else { - std::cmp::min(self.get_table_entry(ns_index - 1).1, end) - }; - (ns_id, start..end) - } -} - -pub struct TxTable {} -impl TxTable { - // Parse `TxTableEntry::byte_len()`` bytes from `raw_payload`` starting at `offset` into a `TxTableEntry` - fn get_len(raw_payload: &[u8], offset: usize) -> TxTableEntry { - let end = std::cmp::min( - offset.saturating_add(TxTableEntry::byte_len()), - raw_payload.len(), - ); - let start = std::cmp::min(offset, end); - let tx_table_len_range = start..end; - let mut entry_bytes = [0u8; TxTableEntry::byte_len()]; - entry_bytes[..tx_table_len_range.len()].copy_from_slice(&raw_payload[tx_table_len_range]); - TxTableEntry::from_bytes_array(entry_bytes) - } - - // Parse the table length from the beginning of the tx table inside `ns_bytes`. - // - // Returned value is guaranteed to be no larger than the number of tx table entries that could possibly fit into `ns_bytes`. - // TODO tidy this is a sloppy wrapper for get_len - pub(crate) fn get_tx_table_len(ns_bytes: &[u8]) -> usize { - std::cmp::min( - Self::get_len(ns_bytes, 0).try_into().unwrap_or(0), - (ns_bytes.len().saturating_sub(TxTableEntry::byte_len())) / TxTableEntry::byte_len(), - ) - } - - // returns tx_offset - // if tx_index would reach beyond ns_bytes then return 0. - // tx_offset is not checked, could be anything - fn get_table_entry(ns_bytes: &[u8], tx_index: usize) -> usize { - // get the range for tx_offset bytes in tx table - let tx_offset_range = { - let start = std::cmp::min( - tx_index - .saturating_add(1) - .saturating_mul(TxTableEntry::byte_len()), - ns_bytes.len(), - ); - let end = std::cmp::min( - start.saturating_add(TxTableEntry::byte_len()), - ns_bytes.len(), - ); - start..end - }; - - // parse tx_offset bytes from tx table - let mut tx_offset_bytes = [0u8; TxTableEntry::byte_len()]; - tx_offset_bytes[..tx_offset_range.len()].copy_from_slice(&ns_bytes[tx_offset_range]); - usize::try_from(TxTableEntry::from_bytes(&tx_offset_bytes).unwrap_or(TxTableEntry::zero())) - .unwrap_or(0) - } - - /// Ensures that the returned range is valid: `start <= end <= ns_bytes`. - pub fn get_payload_range(ns_bytes: &[u8], tx_idx: usize, tx_len: usize) -> Range { - let tx_payloads_offset = tx_len - .saturating_add(1) - .saturating_mul(TxTableEntry::byte_len()); - - let end = std::cmp::min( - TxTable::get_table_entry(ns_bytes, tx_idx).saturating_add(tx_payloads_offset), - ns_bytes.len(), - ); - - let start = if tx_idx == 0 { - tx_payloads_offset - } else { - std::cmp::min( - TxTable::get_table_entry(ns_bytes, tx_idx - 1).saturating_add(tx_payloads_offset), - end, - ) - }; - - start..end - } -} -#[cfg(test)] -pub(super) mod test { - use crate::block::entry::TxTableEntry; - use crate::block::payload::TableWordTraits; - use crate::block::tables::{Table, TxTable}; - use std::marker::PhantomData; - - pub struct TxTableTest { - raw_payload: Vec, - phantom: PhantomData, - } - - impl Table for TxTableTest { - fn get_table_len(&self, offset: usize) -> TxTableEntry { - TxTable::get_len(&self.raw_payload, offset) - } - } - impl TxTableTest { - #[cfg(test)] - pub fn from_entries(entries: &[usize]) -> Self { - let tx_table_byte_len = entries.len() + 1; - let mut tx_table = Vec::with_capacity(tx_table_byte_len); - tx_table.extend(TxTableEntry::from_usize(entries.len()).to_bytes()); - for entry in entries { - tx_table.extend(TxTableEntry::from_usize(*entry).to_bytes()); - } - - Self { - raw_payload: tx_table, - phantom: Default::default(), - } - } - - pub fn get_payload(&self) -> Vec { - self.raw_payload.clone() - } - } -} diff --git a/sequencer/src/block/test.rs b/sequencer/src/block/test.rs new file mode 100644 index 000000000..233dec23d --- /dev/null +++ b/sequencer/src/block/test.rs @@ -0,0 +1,207 @@ +use crate::{ + block::{ + full_payload::{NsProof, Payload}, + namespace_payload::TxProof, + }, + chain_config::BlockSize, + ChainConfig, NamespaceId, NodeState, Transaction, ValidatedState, +}; +use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; +use hotshot::traits::BlockPayload; +use hotshot_query_service::availability::QueryablePayload; +use hotshot_types::{traits::EncodeBytes, vid::vid_scheme}; +use jf_vid::VidScheme; +use rand::RngCore; +use std::collections::HashMap; + +#[async_std::test] +async fn basic_correctness() { + // play with this + let test_cases = vec![ + vec![vec![5, 8, 8], vec![7, 9, 11], vec![10, 5, 8]], // 3 non-empty namespaces + ]; + + setup_logging(); + setup_backtrace(); + let mut rng = jf_utils::test_rng(); + let valid_tests = ValidTest::many_from_tx_lengths(test_cases, &mut rng); + + let mut vid = vid_scheme(10); + + for mut test in valid_tests { + let mut all_txs = test.all_txs(); + tracing::info!("test case {} nss {} txs", test.nss.len(), all_txs.len()); + + let block = + Payload::from_transactions(test.all_txs(), &Default::default(), &Default::default()) + .await + .unwrap() + .0; + tracing::info!( + "ns_table {:?}, payload {:?}", + block.ns_table().encode(), + block.encode() + ); + + // test correct number of nss, txs + assert_eq!(block.ns_table().iter().count(), test.nss.len()); + assert_eq!(block.len(block.ns_table()), all_txs.len()); + assert_eq!(block.iter(block.ns_table()).count(), all_txs.len()); + + tracing::info!("all_txs {:?}", all_txs); + + let (vid_commit, vid_common) = { + let disperse_data = vid.disperse(block.encode()).unwrap(); + (disperse_data.commit, disperse_data.common) + }; + + // test iterate over all txs + for tx_index in block.iter(block.ns_table()) { + let tx = block.transaction(&tx_index).unwrap(); + tracing::info!("tx {:?}, {:?}", tx_index, tx); + + // warning: linear search for a tx + let test_tx = all_txs.remove(all_txs.iter().position(|t| t == &tx).unwrap()); + assert_eq!(tx, test_tx); + + let tx_proof2 = { + let (tx2, tx_proof) = TxProof::new(&tx_index, &block, &vid_common).unwrap(); + assert_eq!(tx, tx2); + tx_proof + }; + assert!(tx_proof2 + .verify(block.ns_table(), &tx, &vid_commit, &vid_common) + .unwrap()); + } + assert!( + all_txs.is_empty(), + "not all test txs consumed by block.iter" + ); + + // test iterate over all namespaces + for ns_index in block.ns_table().iter() { + let ns_id = block.ns_table().read_ns_id(&ns_index).unwrap(); + tracing::info!("test ns_id {ns_id}"); + + let txs = test + .nss + .remove(&ns_id) + .expect("block ns_id missing from test"); + + let ns_proof = NsProof::new(&block, &ns_index, &vid_common) + .expect("namespace_with_proof should succeed"); + + let (ns_proof_txs, ns_proof_ns_id) = ns_proof + .verify(block.ns_table(), &vid_commit, &vid_common) + .unwrap_or_else(|| panic!("namespace {} proof verification failure", ns_id)); + + assert_eq!(ns_proof_ns_id, ns_id); + assert_eq!(ns_proof_txs, txs); + } + assert!( + test.nss.is_empty(), + "not all test namespaces consumed by ns_iter" + ); + } +} + +#[async_std::test] +async fn enforce_max_block_size() { + setup_logging(); + setup_backtrace(); + let test_case = vec![vec![5, 8, 8], vec![7, 9, 11], vec![10, 5, 8]]; + let payload_byte_len_expected: usize = 119; + let ns_table_byte_len_expected: usize = 28; + + let mut rng = jf_utils::test_rng(); + let test = ValidTest::from_tx_lengths(test_case, &mut rng); + let tx_count_expected = test.all_txs().len(); + + let chain_config = ChainConfig { + max_block_size: BlockSize::from( + (payload_byte_len_expected + ns_table_byte_len_expected) as u64, + ), + ..Default::default() + }; + + // test: actual block size equals max block size + let instance_state = NodeState::default().with_chain_config(chain_config); + + let validated_state = ValidatedState { + chain_config: chain_config.into(), + ..Default::default() + }; + let block = Payload::from_transactions(test.all_txs(), &validated_state, &instance_state) + .await + .unwrap() + .0; + assert_eq!(block.encode().len(), payload_byte_len_expected); + assert_eq!(block.ns_table().encode().len(), ns_table_byte_len_expected); + assert_eq!(block.len(block.ns_table()), tx_count_expected); + + // test: actual block size exceeds max block size, so 1 tx is dropped + // WARN log should be emitted + + let chain_config = ChainConfig { + max_block_size: BlockSize::from( + (payload_byte_len_expected + ns_table_byte_len_expected - 1) as u64, + ), + ..Default::default() + }; + let instance_state = NodeState::default().with_chain_config(chain_config); + + let validated_state = ValidatedState { + chain_config: chain_config.into(), + ..Default::default() + }; + + let block = Payload::from_transactions(test.all_txs(), &validated_state, &instance_state) + .await + .unwrap() + .0; + assert!(block.encode().len() < payload_byte_len_expected); + assert_eq!(block.ns_table().encode().len(), ns_table_byte_len_expected); + assert_eq!(block.len(block.ns_table()), tx_count_expected - 1); +} + +// TODO lots of infra here that could be reused in other tests. +pub struct ValidTest { + nss: HashMap>, +} + +impl ValidTest { + pub fn from_tx_lengths(tx_lengths: Vec>, rng: &mut R) -> Self + where + R: RngCore, + { + let mut nss = HashMap::new(); + for tx_lens in tx_lengths.into_iter() { + let ns_id = NamespaceId::random(rng); + for len in tx_lens { + let ns: &mut Vec<_> = nss.entry(ns_id).or_default(); + ns.push(Transaction::new(ns_id, random_bytes(len, rng))); + } + } + Self { nss } + } + + pub fn many_from_tx_lengths(test_cases: Vec>>, rng: &mut R) -> Vec + where + R: RngCore, + { + test_cases + .into_iter() + .map(|t| Self::from_tx_lengths(t, rng)) + .collect() + } + + pub fn all_txs(&self) -> Vec { + self.nss.iter().flat_map(|(_, txs)| txs.clone()).collect() + } +} + +fn random_bytes(len: usize, rng: &mut R) -> Vec { + let mut result = vec![0; len]; + rng.fill_bytes(&mut result); + result +} diff --git a/sequencer/src/block/tx_iterator.rs b/sequencer/src/block/tx_iterator.rs deleted file mode 100644 index 5d8d9d82a..000000000 --- a/sequencer/src/block/tx_iterator.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::ops::Range; - -use crate::block::payload::{Payload, TableWordTraits}; -use crate::block::tables::{NameSpaceTable, TxTable}; -use serde::{Deserialize, Serialize}; - -/// TODO do we really need `PartialOrd`, `Ord` here? -/// Could the `Ord` bound be removed from `QueryablePayload::TransactionIndex`?` -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct TxIndex { - pub ns_idx: usize, - pub tx_idx: usize, -} - -/// TODO Decompose this iterator into -/// - a tx iterator `T` over only 1 namespace -/// - a namespace-tx iterator that reuses `T` over all namespaces -pub struct TxIterator<'a, TableWord: TableWordTraits> { - ns_idx: usize, // simpler than using `Peekable` - ns_iter: Range, - tx_iter: Range, - block_payload: &'a Payload, - ns_table: &'a NameSpaceTable, -} - -impl<'a, TableWord: TableWordTraits> TxIterator<'a, TableWord> { - pub(super) fn new( - ns_table: &'a NameSpaceTable, - block_payload: &'a Payload, - ) -> Self { - Self { - ns_idx: 0, // arbitrary value, changed in first call to next() - ns_iter: 0..ns_table.len(), - tx_iter: 0..0, // empty range - block_payload, - ns_table, - } - } -} - -impl<'a, TableWord: TableWordTraits> Iterator for TxIterator<'a, TableWord> { - type Item = TxIndex; - - fn next(&mut self) -> Option { - if let Some(tx_idx) = self.tx_iter.next() { - // we still have txs left to consume in current ns - Some(TxIndex { - ns_idx: self.ns_idx, - tx_idx, - }) - } else { - // move to the next name space - let payload = &self.block_payload.raw_payload; - for ns_idx in self.ns_iter.by_ref() { - self.ns_idx = ns_idx; - let ns_range = self.ns_table.get_payload_range(ns_idx, payload.len()).1; - let tx_table_len = TxTable::get_tx_table_len(&payload[ns_range]); - self.tx_iter = 0..tx_table_len; - if let Some(tx_idx) = self.tx_iter.next() { - return Some(TxIndex { ns_idx, tx_idx }); - } - } - None // all namespaces consumed - } - } -} diff --git a/sequencer/src/block/uint_bytes.rs b/sequencer/src/block/uint_bytes.rs new file mode 100644 index 000000000..2296a8182 --- /dev/null +++ b/sequencer/src/block/uint_bytes.rs @@ -0,0 +1,231 @@ +//! Serialization (and deserialization) of primitive unsigned integer types to +//! (and from) an arbitrary fixed-length byte array. +//! +use paste::paste; +use std::mem::size_of; + +// Use an ugly macro because it's difficult or impossible to be generic over +// primitive types such as `usize`, `u64`. +macro_rules! uint_bytes_impl { + ($T:ty) => { + paste! { + /// Serialize `n` into `BYTE_LEN` bytes in little-endian form, padding with + /// 0 as needed. + /// + /// # Panics + /// If `n` cannot fit into `BYTE_LEN` bytes. + pub fn [<$T _to_bytes>](n: $T) -> [u8; BYTE_LEN] { + if size_of::<$T>() > BYTE_LEN { + assert!( + [<$T _fits>](n, BYTE_LEN), + "n {n} cannot fit into {BYTE_LEN} bytes" + ); + n.to_le_bytes()[..BYTE_LEN].try_into().unwrap() // panic is impossible + } else { + // convert `n` to bytes and pad with 0 + let mut result = [0; BYTE_LEN]; + result[..size_of::<$T>()].copy_from_slice(&n.to_le_bytes()[..]); + result + } + } + + /// Deserialize `bytes` in little-endian form into a `$T`, padding with 0 + /// as needed. + /// + /// # Panics + /// If `bytes.len()` is too large to fit into a `$T`. + pub fn [<$T _from_bytes>](bytes: &[u8]) -> $T { + assert!(bytes.len() <= BYTE_LEN, "bytes len {} exceeds BYTE_LEN {BYTE_LEN}", bytes.len()); + assert!( + BYTE_LEN <= size_of::<$T>(), + "BYTE_LEN {BYTE_LEN} cannot fit into {}", + stringify!($T) + ); + let mut [<$T _bytes>] = [0; size_of::<$T>()]; + [<$T _bytes>][..bytes.len()].copy_from_slice(bytes); + $T::from_le_bytes([<$T _bytes>]) + } + + /// Return the largest `$T` value that can fit into `byte_len` bytes. + pub const fn [<$T _max_from_byte_len>](byte_len: usize) -> $T { + if byte_len >= size_of::<$T>() { + $T::MAX + } else { + // overflow cannot occur because `byte_len < size_of::<$T>()` + (1 << (byte_len * 8)) - 1 + } + } + + /// Can `n` fit into `byte_len` bytes? + pub const fn [<$T _fits>](n: $T, byte_len: usize) -> bool { + n <= [<$T _max_from_byte_len>](byte_len) + } + } + }; + } + +uint_bytes_impl!(usize); +uint_bytes_impl!(u32); + +/// Impl [`serde`] for type `$T` with methods named `$to_bytes`, `$from_bytes` +/// of the form +/// ```ignore +/// $T::$to_bytes(&self) -> $B +/// $T::$from_bytes(bytes: &[u8]) -> Self +/// ``` +/// where `$B` is any type that impls [`serde::Deserialize`] and has a method +/// `as_ref` of the form +/// ```ignore +/// $B::as_ref(&self) -> &[u8] +/// ``` +/// Typical examples of `$B` include array `[u8; N]`, slice `&[u8]`, or +/// `Vec`. +macro_rules! bytes_serde_impl { + ($T:ty, $to_bytes:ident, $B:ty, $from_bytes:ident) => { + impl Serialize for $T { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.$to_bytes().serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for $T { + fn deserialize(deserializer: D) -> Result<$T, D::Error> + where + D: Deserializer<'de>, + { + <$B as Deserialize>::deserialize(deserializer) + .map(|bytes| <$T>::$from_bytes(bytes.as_ref())) + } + } + }; +} + +pub(super) use bytes_serde_impl; + +#[cfg(test)] +mod test { + use fluent_asserter::prelude::*; + use paste::paste; + use std::mem::size_of; + + macro_rules! uint_bytes_test_impl { + ($T:ty) => { + paste! { + use super::{[<$T _max_from_byte_len>], [<$T _to_bytes>], [<$T _from_bytes>]}; + + #[test] + fn [<$T _max_from_byte_len_correctness>]() { + // test byte lengths 0 to size_of::<$T>() + let mut bytes = [0; size_of::<$T>()]; + assert_eq!([<$T _max_from_byte_len>](0), 0); + for i in 0..bytes.len() { + bytes[i] = 0xff; + assert_eq!([<$T _max_from_byte_len>](i + 1).to_le_bytes(), bytes); + } + + // test byte lengths size_of::<$T>() to twice that length + for i in size_of::<$T>()..2 * size_of::<$T>() { + assert_eq!([<$T _max_from_byte_len>](i + 1), $T::MAX); + } + } + + #[test] + fn [<$T _to_bytes_correctness>]() { + // byte length 0 + assert_eq!([<$T _to_bytes>](0), [0; 0]); + assert_that_code!(|| [<$T _to_bytes>]::<0>(1)).panics(); + + // byte length 1 + assert_eq!([<$T _to_bytes>](0), [0; 1]); + assert_eq!([<$T _to_bytes>](255), [255; 1]); + assert_that_code!(|| [<$T _to_bytes>]::<1>(256)).panics(); + + // byte length 2 + assert_eq!([<$T _to_bytes>](0), [0; 2]); + assert_eq!([<$T _to_bytes>](65535), [255; 2]); + assert_that_code!(|| [<$T _to_bytes>]::<2>(65536)).panics(); + + // byte length size_of::<$T>() + assert_eq!([<$T _to_bytes>](0), [0; size_of::<$T>()]); + assert_eq!([<$T _to_bytes>]($T::MAX), [255; size_of::<$T>()]); + + // byte length size_of::<$T>() + 1 + assert_eq!([<$T _to_bytes>](0), [0; size_of::<$T>() + 1]); + let [<$T _max_bytes>] = { + let mut bytes = [255; size_of::<$T>() + 1]; + bytes[bytes.len() - 1] = 0; + bytes + }; + assert_eq!([<$T _to_bytes>]($T::MAX), [<$T _max_bytes>]); + } + + #[test] + fn [<$T _from_bytes_correctness>]() { + let bytes = [255; size_of::<$T>() + 1]; + + // It would be nice to iterate through + // `0..size_of::<$T>()` but this is not possible with + // const generics for `[<$T _from_bytes>]`. We could + // use `seq-macro` crate but it requires an integer + // literal whereas our range includes `size_of::<$T>()`. + // + // Instead we just hard code four constants: + // `0`, `1`, `size_of::<$T>() - 1`, `size_of::<$T>()`. + assert_eq!( + [<$T _from_bytes>]::<0>(&bytes[..0]), + [<$T _max_from_byte_len>](0) + ); + assert_eq!( + [<$T _from_bytes>]::<1>(&bytes[..1]), + [<$T _max_from_byte_len>](1) + ); + assert_eq!( + [<$T _from_bytes>]::<{size_of::<$T>() - 1}>(&bytes[..size_of::<$T>() - 1]), + [<$T _max_from_byte_len>](size_of::<$T>() - 1) + ); + assert_eq!( + [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..size_of::<$T>()]), + [<$T _max_from_byte_len>](size_of::<$T>()) + ); + + assert_that_code!(|| [<$T _from_bytes>]::<{size_of::<$T>() + 1}>(&bytes[..])).panics(); + } + + #[test] + fn [<$T _from_bytes_allows_smaller_byte_lens>]() { + // This test same as `xxx_from_bytes_correctness` except + // we set the const param `BYTE_LEN` to + // `size_of::<$T>()` in all cases. Why? To ensure that + // `xxx_from_bytes` allows its arg to have length + // smaller than `BYTE_LEN`. + let bytes = [255; size_of::<$T>() + 1]; + + assert_eq!( + [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..0]), + [<$T _max_from_byte_len>](0) + ); + assert_eq!( + [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..1]), + [<$T _max_from_byte_len>](1) + ); + assert_eq!( + [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..size_of::<$T>() - 1]), + [<$T _max_from_byte_len>](size_of::<$T>() - 1) + ); + assert_eq!( + [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..size_of::<$T>()]), + [<$T _max_from_byte_len>](size_of::<$T>()) + ); + + assert_that_code!(|| [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..])).panics(); + } + } + }; + } + + uint_bytes_test_impl!(usize); + uint_bytes_test_impl!(u32); +} diff --git a/sequencer/src/catchup.rs b/sequencer/src/catchup.rs index 90a4eca0e..beb9aff19 100644 --- a/sequencer/src/catchup.rs +++ b/sequencer/src/catchup.rs @@ -1,17 +1,114 @@ use crate::{ - api::endpoints::{AccountQueryData, BlocksFrontier}, + api::{data_source::CatchupDataSource, AccountQueryData, BlocksFrontier}, + options::{parse_duration, Ratio}, + persistence::PersistenceOptions, state::{BlockMerkleTree, FeeAccount, FeeMerkleCommitment}, + ChainConfig, }; +use anyhow::{bail, Context}; +use async_std::{sync::RwLock, task::sleep}; use async_trait::async_trait; +use clap::Parser; +use committable::Commitment; +use futures::future::{BoxFuture, FutureExt, TryFutureExt}; use hotshot_types::{data::ViewNumber, traits::node_implementation::ConsensusTime as _}; -use jf_primitives::merkle_tree::{ForgetableMerkleTreeScheme, MerkleTreeScheme}; +use jf_merkle_tree::{prelude::MerkleNode, ForgetableMerkleTreeScheme, MerkleTreeScheme}; +use rand::Rng; use serde::de::DeserializeOwned; -use std::{sync::Arc, time::Duration}; +use std::{cmp::min, fmt::Debug, sync::Arc, time::Duration}; use surf_disco::Request; use tide_disco::error::ServerError; use url::Url; use vbs::version::StaticVersionType; +#[derive(Clone, Copy, Debug, Parser, PartialEq, Eq, PartialOrd, Ord)] +pub struct BackoffParams { + /// Exponential backoff exponent. + #[clap( + long = "catchup-backoff-factor", + env = "ESPRESSO_SEQUENCER_CATCHUP_BACKOFF_FACTOR", + default_value = "4" + )] + factor: u32, + + /// Exponential backoff base delay. + #[clap( + long = "catchup-base-retry-delay", + env = "ESPRESSO_SEQUENCER_CATCHUP_BASE_RETRY_DELAY", + default_value = "20ms", + value_parser = parse_duration + )] + base: Duration, + + /// Exponential max delay. + #[clap( + long = "catchup-max-retry-delay", + env = "ESPRESSO_SEQUENCER_CATCHUP_MAX_RETRY_DELAY", + default_value = "5s", + value_parser = parse_duration + )] + max: Duration, + + /// Exponential backoff jitter as a ratio of the backoff delay, numerator:denominator. + #[clap( + long = "catchup-backoff-jitter", + env = "ESPRESSO_SEQUENCER_CATCHUP_BACKOFF_JITTER", + default_value = "1:10" + )] + jitter: Ratio, +} + +impl Default for BackoffParams { + fn default() -> Self { + Self::parse_from(std::iter::empty::()) + } +} + +impl BackoffParams { + async fn retry( + &self, + mut state: S, + f: impl for<'a> Fn(&'a mut S) -> BoxFuture<'a, anyhow::Result>, + ) -> T { + let mut delay = self.base; + loop { + match f(&mut state).await { + Ok(res) => break res, + Err(err) => { + tracing::warn!( + "Retryable operation failed, will retry after {delay:?}: {err:#}" + ); + sleep(delay).await; + delay = self.backoff(delay); + } + } + } + } + + #[must_use] + fn backoff(&self, delay: Duration) -> Duration { + if delay >= self.max { + return self.max; + } + + let mut rng = rand::thread_rng(); + + // Increase the backoff by the backoff factor. + let ms = (delay * self.factor).as_millis() as u64; + + // Sample a random jitter factor in the range [0, self.jitter]. + let jitter_num = rng.gen_range(0..self.jitter.numerator); + let jitter_den = self.jitter.denominator; + + // Increase the delay by the jitter factor. + let jitter = ms * jitter_num / jitter_den; + let delay = Duration::from_millis(ms + jitter); + + // Bound the delay by the maximum. + min(delay, self.max) + } +} + // This newtype is probably not worth having. It's only used to be able to log // URLs before doing requests. #[derive(Debug, Clone)] @@ -35,178 +132,473 @@ impl Client { #[async_trait] pub trait StateCatchup: Send + Sync + std::fmt::Debug { + /// Try to fetch the given account state, failing without retrying if unable. + async fn try_fetch_account( + &self, + height: u64, + view: ViewNumber, + fee_merkle_tree_root: FeeMerkleCommitment, + account: FeeAccount, + ) -> anyhow::Result; + + /// Fetch the given list of accounts, retrying on transient errors. async fn fetch_accounts( &self, + height: u64, view: ViewNumber, fee_merkle_tree_root: FeeMerkleCommitment, accounts: Vec, - ) -> anyhow::Result>; + ) -> anyhow::Result> { + let mut ret = vec![]; + for account in accounts { + let account = self + .backoff() + .retry(self, |provider| { + provider + .try_fetch_account(height, view, fee_merkle_tree_root, account) + .map_err(|err| err.context("fetching account {account}")) + .boxed() + }) + .await; + ret.push(account); + } + Ok(ret) + } - async fn remember_blocks_merkle_tree( + /// Try to fetch and remember the blocks frontier, failing without retrying if unable. + async fn try_remember_blocks_merkle_tree( &self, + height: u64, view: ViewNumber, mt: &mut BlockMerkleTree, ) -> anyhow::Result<()>; + + /// Fetch and remember the blocks frontier, retrying on transient errors. + async fn remember_blocks_merkle_tree( + &self, + height: u64, + view: ViewNumber, + mt: &mut BlockMerkleTree, + ) -> anyhow::Result<()> { + self.backoff() + .retry(mt, |mt| { + self.try_remember_blocks_merkle_tree(height, view, mt) + .map_err(|err| err.context("fetching frontier")) + .boxed() + }) + .await; + Ok(()) + } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result; + + async fn fetch_chain_config(&self, commitment: Commitment) -> ChainConfig { + self.backoff() + .retry(self, |provider| { + provider + .try_fetch_chain_config(commitment) + .map_err(|err| err.context("fetching chain config")) + .boxed() + }) + .await + } + + fn backoff(&self) -> &BackoffParams; +} + +/// A catchup implementation that falls back to a remote provider, but prefers a local provider when +/// supported. +pub(crate) async fn local_and_remote( + local_opt: impl PersistenceOptions, + remote: impl StateCatchup + 'static, +) -> Arc { + match local_opt.create_catchup_provider(*remote.backoff()).await { + Ok(local) => Arc::new(vec![local, Arc::new(remote)]), + Err(err) => { + tracing::warn!("not using local catchup: {err:#}"); + Arc::new(remote) + } + } } #[derive(Debug, Clone, Default)] pub struct StatePeers { clients: Vec>, - interval: Duration, + backoff: BackoffParams, } impl StatePeers { - pub fn from_urls(urls: Vec) -> Self { + pub fn from_urls(urls: Vec, backoff: BackoffParams) -> Self { if urls.is_empty() { panic!("Cannot create StatePeers with no peers"); } Self { clients: urls.into_iter().map(Client::new).collect(), - interval: Duration::from_secs(1), + backoff, } } +} +#[async_trait] +impl StateCatchup for StatePeers { #[tracing::instrument(skip(self))] - async fn fetch_account( + async fn try_fetch_account( &self, + height: u64, view: ViewNumber, fee_merkle_tree_root: FeeMerkleCommitment, account: FeeAccount, - ) -> AccountQueryData { - if self.clients.is_empty() { - panic!("No peers to fetch account from"); + ) -> anyhow::Result { + for client in self.clients.iter() { + tracing::info!("Fetching account {account:?} from {}", client.url); + match client + .get::(&format!( + "catchup/{height}/{}/account/{account}", + view.u64(), + )) + .send() + .await + { + Ok(res) => match res.proof.verify(&fee_merkle_tree_root) { + Ok(_) => return Ok(res), + Err(err) => tracing::warn!("Error verifying account proof: {}", err), + }, + Err(err) => { + tracing::warn!("Error fetching account from peer: {}", err); + } + } } - loop { - for client in self.clients.iter() { - tracing::info!( - "Fetching account {account:?} for view {view:?} from {}", - client.url - ); - match client - .get::(&format!( - "catchup/{}/account/{account}", - view.get_u64(), - )) - .send() - .await - { - Ok(res) => match res.proof.verify(&fee_merkle_tree_root) { - Ok(_) => return res, - Err(err) => tracing::warn!("Error verifying account proof: {}", err), - }, - Err(err) => { - tracing::warn!("Error fetching account from peer: {}", err); + bail!("Could not fetch account from any peer"); + } + + #[tracing::instrument(skip(self, mt), height = mt.num_leaves())] + async fn try_remember_blocks_merkle_tree( + &self, + height: u64, + view: ViewNumber, + mt: &mut BlockMerkleTree, + ) -> anyhow::Result<()> { + for client in self.clients.iter() { + tracing::info!("Fetching frontier from {}", client.url); + match client + .get::(&format!("catchup/{height}/{}/blocks", view.u64())) + .send() + .await + { + Ok(frontier) => { + let Some(elem) = frontier.elem() else { + tracing::warn!("Provided frontier is missing leaf element"); + continue; + }; + match mt.remember(mt.num_leaves() - 1, *elem, &frontier) { + Ok(_) => return Ok(()), + Err(err) => { + tracing::warn!("Error verifying block proof: {}", err); + continue; + } } } + Err(err) => { + tracing::warn!("Error fetching blocks from peer: {}", err); + } } - tracing::warn!("Could not fetch account from any peer, retrying"); - async_std::task::sleep(self.interval).await; } + bail!("Could not fetch frontier from any peer"); + } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + for client in self.clients.iter() { + tracing::info!("Fetching chain config from {}", client.url); + match client + .get::(&format!("catchup/chain-config/{}", commitment)) + .send() + .await + { + Ok(cf) => { + return Ok(cf); + } + Err(err) => { + tracing::warn!("Error fetching chain config from peer: {}", err); + } + } + } + bail!("Could not fetch chain config from any peer"); + } + + fn backoff(&self) -> &BackoffParams { + &self.backoff + } +} + +#[derive(Debug)] +pub(crate) struct SqlStateCatchup { + db: Arc>, + backoff: BackoffParams, +} + +impl SqlStateCatchup { + pub(crate) fn new(db: Arc>, backoff: BackoffParams) -> Self { + Self { db, backoff } } } #[async_trait] -impl StateCatchup for StatePeers { - async fn fetch_accounts( +impl StateCatchup for SqlStateCatchup +where + T: CatchupDataSource + Debug + Send + Sync, +{ + #[tracing::instrument(skip(self))] + async fn try_fetch_account( &self, + block_height: u64, view: ViewNumber, - fee_merkle_tree_root: FeeMerkleCommitment, - accounts: Vec, - ) -> anyhow::Result> { - let mut ret = vec![]; - for account in accounts { - tracing::info!("Fetching account {account:?} for view {view:?}"); - ret.push( - self.fetch_account(view, fee_merkle_tree_root, account) - .await, - ) - } - Ok(ret) + _fee_merkle_tree_root: FeeMerkleCommitment, + account: FeeAccount, + ) -> anyhow::Result { + self.db + .read() + .await + .get_account(block_height, view, account.into()) + .await } - #[tracing::instrument(skip(self, mt), height = mt.num_leaves())] - async fn remember_blocks_merkle_tree( + #[tracing::instrument(skip(self))] + async fn try_remember_blocks_merkle_tree( &self, + bh: u64, view: ViewNumber, mt: &mut BlockMerkleTree, ) -> anyhow::Result<()> { - if self.clients.is_empty() { - panic!("No peers to fetch frontier from"); + if bh == 0 { + return Ok(()); } - loop { - for client in self.clients.iter() { - tracing::info!("Fetching frontier from {}", client.url); - match client - .get::(&format!("catchup/{}/blocks", view.get_u64())) - .send() - .await - { - Ok(frontier) => { - let Some(elem) = frontier.elem() else { - tracing::warn!("Provided frontier is missing leaf element"); - continue; - }; - match mt.remember(mt.num_leaves() - 1, *elem, &frontier) { - Ok(_) => return Ok(()), - Err(err) => { - tracing::warn!("Error verifying block proof: {}", err); - continue; - } - } - } - Err(err) => { - tracing::warn!("Error fetching blocks from peer: {}", err); - } - } - } - tracing::warn!("Could not fetch frontier from any peer, retrying"); - async_std::task::sleep(self.interval).await; + + let proof = self.db.read().await.get_frontier(bh, view).await?; + match proof + .proof + .first() + .context(format!("empty proof for frontier at height {bh}"))? + { + MerkleNode::Leaf { pos, elem, .. } => mt + .remember(pos, elem, proof.clone()) + .context("failed to remember proof"), + _ => bail!("invalid proof"), } } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + self.db.read().await.get_chain_config(commitment).await + } + + fn backoff(&self) -> &BackoffParams { + &self.backoff + } } #[async_trait] impl StateCatchup for Box { + async fn try_fetch_account( + &self, + height: u64, + view: ViewNumber, + fee_merkle_tree_root: FeeMerkleCommitment, + account: FeeAccount, + ) -> anyhow::Result { + (**self) + .try_fetch_account(height, view, fee_merkle_tree_root, account) + .await + } + async fn fetch_accounts( &self, + height: u64, view: ViewNumber, fee_merkle_tree_root: FeeMerkleCommitment, accounts: Vec, ) -> anyhow::Result> { (**self) - .fetch_accounts(view, fee_merkle_tree_root, accounts) + .fetch_accounts(height, view, fee_merkle_tree_root, accounts) + .await + } + + async fn try_remember_blocks_merkle_tree( + &self, + height: u64, + view: ViewNumber, + mt: &mut BlockMerkleTree, + ) -> anyhow::Result<()> { + (**self) + .try_remember_blocks_merkle_tree(height, view, mt) .await } async fn remember_blocks_merkle_tree( &self, + height: u64, view: ViewNumber, mt: &mut BlockMerkleTree, ) -> anyhow::Result<()> { - (**self).remember_blocks_merkle_tree(view, mt).await + (**self).remember_blocks_merkle_tree(height, view, mt).await + } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + (**self).try_fetch_chain_config(commitment).await + } + + async fn fetch_chain_config(&self, commitment: Commitment) -> ChainConfig { + (**self).fetch_chain_config(commitment).await + } + + fn backoff(&self) -> &BackoffParams { + (**self).backoff() } } #[async_trait] impl StateCatchup for Arc { + async fn try_fetch_account( + &self, + height: u64, + view: ViewNumber, + fee_merkle_tree_root: FeeMerkleCommitment, + account: FeeAccount, + ) -> anyhow::Result { + (**self) + .try_fetch_account(height, view, fee_merkle_tree_root, account) + .await + } + async fn fetch_accounts( &self, + height: u64, view: ViewNumber, fee_merkle_tree_root: FeeMerkleCommitment, accounts: Vec, ) -> anyhow::Result> { (**self) - .fetch_accounts(view, fee_merkle_tree_root, accounts) + .fetch_accounts(height, view, fee_merkle_tree_root, accounts) + .await + } + + async fn try_remember_blocks_merkle_tree( + &self, + height: u64, + view: ViewNumber, + mt: &mut BlockMerkleTree, + ) -> anyhow::Result<()> { + (**self) + .try_remember_blocks_merkle_tree(height, view, mt) .await } async fn remember_blocks_merkle_tree( &self, + height: u64, view: ViewNumber, mt: &mut BlockMerkleTree, ) -> anyhow::Result<()> { - (**self).remember_blocks_merkle_tree(view, mt).await + (**self).remember_blocks_merkle_tree(height, view, mt).await + } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + (**self).try_fetch_chain_config(commitment).await + } + + async fn fetch_chain_config(&self, commitment: Commitment) -> ChainConfig { + (**self).fetch_chain_config(commitment).await + } + + fn backoff(&self) -> &BackoffParams { + (**self).backoff() + } +} + +/// Catchup from multiple providers tries each provider in a round robin fashion until it succeeds. +#[async_trait] +impl StateCatchup for Vec { + #[tracing::instrument(skip(self))] + async fn try_fetch_account( + &self, + height: u64, + view: ViewNumber, + fee_merkle_tree_root: FeeMerkleCommitment, + account: FeeAccount, + ) -> anyhow::Result { + for provider in self { + match provider + .try_fetch_account(height, view, fee_merkle_tree_root, account) + .await + { + Ok(account) => return Ok(account), + Err(err) => { + tracing::warn!(%account, ?provider, "failed to fetch account: {err:#}"); + } + } + } + + bail!("could not fetch account from any provider"); + } + + #[tracing::instrument(skip(self, mt))] + async fn try_remember_blocks_merkle_tree( + &self, + height: u64, + view: ViewNumber, + mt: &mut BlockMerkleTree, + ) -> anyhow::Result<()> { + for provider in self { + match provider + .try_remember_blocks_merkle_tree(height, view, mt) + .await + { + Ok(()) => return Ok(()), + Err(err) => { + tracing::warn!(?provider, "failed to fetch frontier: {err:#}"); + } + } + } + + bail!("could not fetch account from any provider"); + } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + for provider in self { + match provider.try_fetch_chain_config(commitment).await { + Ok(cf) => return Ok(cf), + Err(err) => { + tracing::warn!(?provider, "failed to fetch chain config: {err:#}"); + } + } + } + + bail!("could not fetch chain config from any provider"); + } + + fn backoff(&self) -> &BackoffParams { + // Use whichever provider's backoff is most conservative. + self.iter() + .map(|p| p.backoff()) + .max() + .expect("provider list not empty") } } @@ -214,17 +606,19 @@ impl StateCatchup for Arc { pub mod mock { use super::*; use crate::state::{FeeAccountProof, ValidatedState}; - use jf_primitives::merkle_tree::MerkleTreeScheme; + use jf_merkle_tree::MerkleTreeScheme; use std::collections::HashMap; #[derive(Debug, Clone, Default)] pub struct MockStateCatchup { + backoff: BackoffParams, state: HashMap>, } impl FromIterator<(ViewNumber, Arc)> for MockStateCatchup { fn from_iter)>>(iter: I) -> Self { Self { + backoff: Default::default(), state: iter.into_iter().collect(), } } @@ -232,29 +626,25 @@ pub mod mock { #[async_trait] impl StateCatchup for MockStateCatchup { - async fn fetch_accounts( + async fn try_fetch_account( &self, + _height: u64, view: ViewNumber, fee_merkle_tree_root: FeeMerkleCommitment, - accounts: Vec, - ) -> anyhow::Result> { - tracing::info!("catchup: fetching account data for view {view:?}"); + account: FeeAccount, + ) -> anyhow::Result { let src = &self.state[&view].fee_merkle_tree; assert_eq!(src.commitment(), fee_merkle_tree_root); - accounts - .into_iter() - .map(|account| { - tracing::info!("catchup: fetching account {account:?} for view {view:?}"); - Ok(FeeAccountProof::prove(src, account.into()) - .unwrap_or_else(|| panic!("Account {account:?} not in memory")) - .into()) - }) - .collect::>() + tracing::info!("catchup: fetching account {account:?} for view {view:?}"); + Ok(FeeAccountProof::prove(src, account.into()) + .unwrap_or_else(|| panic!("Account {account:?} not in memory")) + .into()) } - async fn remember_blocks_merkle_tree( + async fn try_remember_blocks_merkle_tree( &self, + _height: u64, view: ViewNumber, mt: &mut BlockMerkleTree, ) -> anyhow::Result<()> { @@ -274,5 +664,16 @@ pub mod mock { Ok(()) } + + async fn try_fetch_chain_config( + &self, + _commitment: Commitment, + ) -> anyhow::Result { + Ok(ChainConfig::default()) + } + + fn backoff(&self) -> &BackoffParams { + &self.backoff + } } } diff --git a/sequencer/src/chain_config.rs b/sequencer/src/chain_config.rs index 41c52ee80..5140d6ca5 100644 --- a/sequencer/src/chain_config.rs +++ b/sequencer/src/chain_config.rs @@ -1,58 +1,127 @@ -use crate::state::FeeAmount; +use crate::{ + options::parse_size, + state::{FeeAccount, FeeAmount}, +}; use committable::{Commitment, Committable}; -use derive_more::{From, Into}; -use ethers::types::U256; +use derive_more::{Deref, Display, From, Into}; +use ethers::types::{Address, U256}; use itertools::Either; -use sequencer_utils::impl_to_fixed_bytes; +use sequencer_utils::{ + impl_serde_from_string_or_integer, impl_to_fixed_bytes, ser::FromStringOrInteger, +}; use serde::{Deserialize, Serialize}; +use std::str::FromStr; -#[derive(Default, Hash, Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, From, Into)] +#[derive(Default, Hash, Copy, Clone, Debug, Display, PartialEq, Eq, From, Into)] +#[display(fmt = "{_0}")] pub struct ChainId(U256); +impl_serde_from_string_or_integer!(ChainId); impl_to_fixed_bytes!(ChainId, U256); -impl From for ChainId { - fn from(id: u16) -> Self { +impl FromStringOrInteger for ChainId { + type Binary = U256; + type Integer = u64; + + fn from_binary(b: Self::Binary) -> anyhow::Result { + Ok(Self(b)) + } + + fn from_integer(i: Self::Integer) -> anyhow::Result { + Ok(i.into()) + } + + fn from_string(s: String) -> anyhow::Result { + if s.starts_with("0x") { + Ok(Self(U256::from_str(&s)?)) + } else { + Ok(Self(U256::from_dec_str(&s)?)) + } + } + + fn to_binary(&self) -> anyhow::Result { + Ok(self.0) + } + + fn to_string(&self) -> anyhow::Result { + Ok(format!("{self}")) + } +} + +impl From for ChainId { + fn from(id: u64) -> Self { Self(id.into()) } } +#[derive(Hash, Copy, Clone, Debug, Default, Display, PartialEq, Eq, From, Into, Deref)] +#[display(fmt = "{_0}")] +pub struct BlockSize(u64); + +impl_serde_from_string_or_integer!(BlockSize); + +impl FromStringOrInteger for BlockSize { + type Binary = u64; + type Integer = u64; + + fn from_binary(b: Self::Binary) -> anyhow::Result { + Ok(Self(b)) + } + + fn from_integer(i: Self::Integer) -> anyhow::Result { + Ok(Self(i)) + } + + fn from_string(s: String) -> anyhow::Result { + Ok(parse_size(&s)?.into()) + } + + fn to_binary(&self) -> anyhow::Result { + Ok(self.0) + } + + fn to_string(&self) -> anyhow::Result { + Ok(format!("{self}")) + } +} + /// Global variables for an Espresso blockchain. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ChainConfig { /// Espresso chain ID - chain_id: ChainId, + pub chain_id: ChainId, + /// Maximum size in bytes of a block - max_block_size: u64, + pub max_block_size: BlockSize, + /// Minimum fee in WEI per byte of payload - base_fee: FeeAmount, + pub base_fee: FeeAmount, + + /// Fee contract address on L1. + /// + /// This is optional so that fees can easily be toggled on/off, with no need to deploy a + /// contract when they are off. In a future release, after fees are switched on and thoroughly + /// tested, this may be made mandatory. + pub fee_contract: Option
, + + /// Account that receives sequencing fees. + /// + /// This account in the Espresso fee ledger will always receive every fee paid in Espresso, + /// regardless of whether or not their is a `fee_contract` deployed. Once deployed, the fee + /// contract can decide what to do with tokens locked in this account in Espresso. + pub fee_recipient: FeeAccount, } impl Default for ChainConfig { fn default() -> Self { - Self::new( - U256::from(35353), // arbitrarily chosen chain ID - 10240, // 10 kB max_block_size - 0, // no fees - ) - } -} - -impl ChainConfig { - pub fn new( - chain_id: impl Into, - max_block_size: u64, - base_fee: impl Into, - ) -> Self { Self { - chain_id: chain_id.into(), - max_block_size, - base_fee: base_fee.into(), + chain_id: U256::from(35353).into(), // arbitrarily chosen chain ID + max_block_size: 10240.into(), + base_fee: 0.into(), + fee_contract: None, + fee_recipient: Default::default(), } } - pub fn max_block_size(&self) -> u64 { - self.max_block_size - } } impl Committable for ChainConfig { @@ -61,11 +130,17 @@ impl Committable for ChainConfig { } fn commit(&self) -> Commitment { - committable::RawCommitmentBuilder::new(&Self::tag()) + let comm = committable::RawCommitmentBuilder::new(&Self::tag()) .fixed_size_field("chain_id", &self.chain_id.to_fixed_bytes()) - .u64_field("max_block_size", self.max_block_size) + .u64_field("max_block_size", *self.max_block_size) .fixed_size_field("base_fee", &self.base_fee.to_fixed_bytes()) - .finalize() + .fixed_size_field("fee_recipient", &self.fee_recipient.to_fixed_bytes()); + let comm = if let Some(addr) = self.fee_contract { + comm.u64_field("fee_contract", 1).fixed_size_bytes(&addr.0) + } else { + comm.u64_field("fee_contract", 0) + }; + comm.finalize() } } @@ -109,6 +184,76 @@ impl From for ResolvableChainConfig { mod tests { use super::*; + #[test] + fn test_chainid_serde_json_as_decimal() { + let id = ChainId::from(123); + let serialized = serde_json::to_string(&id).unwrap(); + + // The value is serialized as a decimal string. + assert_eq!(serialized, "\"123\""); + + // Deserialization produces the original value + let deserialized: ChainId = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, id); + } + + #[test] + fn test_chainid_serde_json_from_hex() { + // For backwards compatibility, chain ID can also be deserialized from a 0x-prefixed hex + // string. + let id: ChainId = serde_json::from_str("\"0x123\"").unwrap(); + assert_eq!(id, ChainId::from(0x123)); + } + + #[test] + fn test_chainid_serde_json_from_number() { + // For convenience, chain ID can also be deserialized from a decimal number. + let id: ChainId = serde_json::from_str("123").unwrap(); + assert_eq!(id, ChainId::from(123)); + } + + #[test] + fn test_chainid_serde_bincode_unchanged() { + // For non-human-readable formats, ChainId just serializes as the underlying U256. + let n = U256::from(123); + let id = ChainId(n); + assert_eq!( + bincode::serialize(&n).unwrap(), + bincode::serialize(&id).unwrap(), + ); + } + + #[test] + fn test_block_size_serde_json_as_decimal() { + let size = BlockSize::from(123); + let serialized = serde_json::to_string(&size).unwrap(); + + // The value is serialized as a decimal string. + assert_eq!(serialized, "\"123\""); + + // Deserialization produces the original value + let deserialized: BlockSize = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, size); + } + + #[test] + fn test_block_size_serde_json_from_number() { + // For backwards compatibility, block size can also be deserialized from a decimal number. + let size: BlockSize = serde_json::from_str("123").unwrap(); + assert_eq!(size, BlockSize::from(123)); + } + + #[test] + fn test_block_size_serde_bincode_unchanged() { + // For non-human-readable formats, BlockSize just serializes as the underlying u64. + let n = 123u64; + let size = BlockSize(n); + assert_eq!( + bincode::serialize(&n).unwrap(), + bincode::serialize(&size).unwrap(), + ); + } + #[test] fn test_chain_config_equality() { let chain_config = ChainConfig::default(); @@ -118,7 +263,12 @@ mod tests { max_block_size, .. } = chain_config; - let other_config = ChainConfig::new(chain_id, max_block_size, 1); + let other_config = ChainConfig { + chain_id, + max_block_size, + base_fee: 1.into(), + ..Default::default() + }; assert!(chain_config != other_config); } diff --git a/sequencer/src/context.rs b/sequencer/src/context.rs index 694bba38b..ab239c751 100644 --- a/sequencer/src/context.rs +++ b/sequencer/src/context.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use async_std::{ sync::{Arc, RwLock}, task::{spawn, JoinHandle}, @@ -13,8 +14,10 @@ use hotshot::{ Memberships, Networks, SystemContext, }; use hotshot_orchestrator::client::OrchestratorClient; +use hotshot_query_service::Leaf; use hotshot_types::{ consensus::ConsensusMetricsValue, + data::ViewNumber, traits::{election::Membership, metrics::Metrics}, HotShotConfig, }; @@ -24,7 +27,7 @@ use vbs::version::StaticVersionType; use crate::{ network, persistence::SequencerPersistence, state_signature::StateSigner, - static_stake_table_commitment, Node, NodeState, PubKey, SeqTypes, Transaction, + static_stake_table_commitment, Node, NodeState, PubKey, SeqTypes, Transaction, ValidatedState, }; use hotshot_events_service::events_source::{EventConsumer, EventsStreamer}; /// The consensus handle @@ -40,11 +43,7 @@ pub struct SequencerContext< > { /// The consensus handle #[derivative(Debug = "ignore")] - handle: Consensus, - - /// Index of this sequencer node - #[allow(dead_code)] - node_index: u64, + handle: Arc>>, /// Context for generating state signatures. state_signer: Arc>, @@ -67,7 +66,7 @@ pub struct SequencerContext< impl SequencerContext { - #[tracing::instrument(skip_all, fields(node_id))] + #[tracing::instrument(skip_all, fields(node_id = instance_state.node_id))] #[allow(clippy::too_many_arguments)] pub async fn init( config: HotShotConfig, @@ -76,21 +75,16 @@ impl>, state_relay_server: Option, metrics: &dyn Metrics, - node_id: u64, - stake_table_capacity: usize, + stake_table_capacity: u64, _: Ver, ) -> anyhow::Result { let pub_key = config.my_own_validator_config.public_key; tracing::info!(%pub_key, "initializing consensus"); - // Stick our public key and node ID in `metrics` so it is easily accessible via the status - // API. - metrics - .create_label("pub_key".into()) - .set(pub_key.to_string()); + // Stick our node ID in `metrics` so it is easily accessible via the status API. metrics .create_gauge("node_index".into(), None) - .set(node_id as usize); + .set(instance_state.node_id as usize); // Load saved consensus state from storage. let initializer = persistence @@ -116,8 +110,12 @@ impl::new( @@ -130,7 +128,7 @@ impl, persistence: Arc>, - node_index: u64, state_signer: StateSigner, event_streamer: Arc>>, node_state: NodeState, ) -> Self { - let events = handle.get_event_stream(); + let events = handle.event_stream(); let mut ctx = Self { - handle, - node_index, + handle: Arc::new(RwLock::new(handle)), state_signer: Arc::new(state_signer), tasks: Default::default(), detached: false, @@ -208,32 +203,43 @@ impl impl Stream> { - self.handle.get_event_stream() + pub async fn event_stream(&self) -> impl Stream> { + self.handle.read().await.event_stream() } pub async fn submit_transaction(&self, tx: Transaction) -> anyhow::Result<()> { - self.handle.submit_transaction(tx).await?; + self.handle.read().await.submit_transaction(tx).await?; Ok(()) } /// get event streamer - pub fn get_event_streamer(&self) -> Arc>> { + pub fn event_streamer(&self) -> Arc>> { self.events_streamer.clone() } /// Return a reference to the underlying consensus handle. - pub fn consensus(&self) -> &Consensus { - &self.handle + pub fn consensus(&self) -> Arc>> { + Arc::clone(&self.handle) } - pub fn node_state(&self) -> NodeState { - self.node_state.clone() + pub async fn shutdown_consensus(&self) { + self.handle.write().await.shut_down().await + } + + pub async fn decided_leaf(&self) -> Leaf { + self.handle.read().await.decided_leaf().await + } + + pub async fn state(&self, view: ViewNumber) -> Option> { + self.handle.read().await.state(view).await } - /// Return a mutable reference to the underlying consensus handle. - pub fn consensus_mut(&mut self) -> &mut Consensus { - &mut self.handle + pub async fn decided_state(&self) -> Arc { + self.handle.read().await.decided_state().await + } + + pub fn node_state(&self) -> NodeState { + self.node_state.clone() } /// Start participating in consensus. @@ -241,11 +247,11 @@ impl Address { self.fee_account.address() } + + pub fn signer(&self) -> LocalWallet { + LocalWallet::from_bytes(&self.signing_key.to_bytes()).unwrap() + } } impl Hash for EthKeyPair { @@ -108,9 +112,11 @@ impl Ord for EthKeyPair { #[derive(Clone, Debug, Snafu)] pub struct SigningError; +pub type BuilderSignature = Signature; + impl BuilderSignatureKey for FeeAccount { type BuilderPrivateKey = EthKeyPair; - type BuilderSignature = Signature; + type BuilderSignature = BuilderSignature; type SignError = SigningError; fn validate_builder_signature(&self, signature: &Self::BuilderSignature, data: &[u8]) -> bool { @@ -121,7 +127,7 @@ impl BuilderSignatureKey for FeeAccount { private_key: &Self::BuilderPrivateKey, data: &[u8], ) -> Result { - let wallet = LocalWallet::from_bytes(&private_key.signing_key.to_bytes()).unwrap(); + let wallet = private_key.signer(); let message_hash = ethers::utils::hash_message(data); wallet.sign_hash(message_hash).map_err(|_| SigningError) } @@ -190,12 +196,12 @@ mod tests { let sig = FeeAccount::sign_builder_message(&key, msg).unwrap(); assert!(key.fee_account().validate_builder_signature(&sig, msg)); - // Recovery fails if signed with other key + // Validation fails if signed with other key. let other_key = FeeAccount::generated_from_seed_indexed([0u8; 32], 1).1; let sig = FeeAccount::sign_builder_message(&other_key, msg).unwrap(); assert!(!key.fee_account().validate_builder_signature(&sig, msg)); - // Recovery fails if another message was signed + // Validation fails if another message was signed let sig = FeeAccount::sign_builder_message(&key, b"hello world XYZ").unwrap(); assert!(!key.fee_account().validate_builder_signature(&sig, msg)); } diff --git a/sequencer/src/genesis.rs b/sequencer/src/genesis.rs new file mode 100644 index 000000000..9c013090d --- /dev/null +++ b/sequencer/src/genesis.rs @@ -0,0 +1,411 @@ +use crate::{ + l1_client::L1BlockInfo, + state::{FeeAccount, FeeAmount}, + ChainConfig, +}; +use anyhow::Context; +use derive_more::{Display, From, Into}; +use sequencer_utils::{impl_serde_from_string_or_integer, ser::FromStringOrInteger}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{BTreeMap, HashMap}, + path::Path, +}; +use time::{format_description::well_known::Rfc3339 as TimestampFormat, OffsetDateTime}; +use vbs::version::Version; + +/// Initial configuration of an Espresso stake table. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +pub struct StakeTableConfig { + pub capacity: u64, +} + +/// An L1 block from which an Espresso chain should start syncing. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(untagged)] +pub enum L1Finalized { + /// Complete block info. + /// + /// This allows a validator to specify the exact, existing L1 block to start syncing from. A + /// validator that specifies a specific L1 block will not be able to reach consensus with a + /// malicious validator that starts from a different L1 block. + Block(L1BlockInfo), + + /// An L1 block number to sync from. + /// + /// This allows a validator to specify a future L1 block whose hash is not yet known, and start + /// syncing only when a finalized block with the given number becomes available. The configured + /// L1 client will be used to fetch the rest of the block info once available. + Number { number: u64 }, +} + +#[derive(Hash, Copy, Clone, Debug, Display, PartialEq, Eq, From, Into)] +#[display(fmt = "{}", "_0.format(&TimestampFormat).unwrap()")] +pub struct Timestamp(OffsetDateTime); + +impl_serde_from_string_or_integer!(Timestamp); + +impl Default for Timestamp { + fn default() -> Self { + Self::from_integer(0).unwrap() + } +} + +impl Timestamp { + pub fn unix_timestamp(&self) -> u64 { + self.0.unix_timestamp() as u64 + } +} + +impl FromStringOrInteger for Timestamp { + type Binary = u64; + type Integer = u64; + + fn from_binary(b: Self::Binary) -> anyhow::Result { + Self::from_integer(b) + } + + fn from_integer(i: Self::Integer) -> anyhow::Result { + let unix = i.try_into().context("timestamp out of range")?; + Ok(Self( + OffsetDateTime::from_unix_timestamp(unix).context("invalid timestamp")?, + )) + } + + fn from_string(s: String) -> anyhow::Result { + Ok(Self( + OffsetDateTime::parse(&s, &TimestampFormat).context("invalid timestamp")?, + )) + } + + fn to_binary(&self) -> anyhow::Result { + Ok(self.unix_timestamp()) + } + + fn to_string(&self) -> anyhow::Result { + Ok(format!("{self}")) + } +} + +/// Information about the genesis state which feeds into the genesis block header. +#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] +pub struct GenesisHeader { + pub timestamp: Timestamp, +} + +/// Genesis of an Espresso chain. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Genesis { + pub chain_config: ChainConfig, + pub stake_table: StakeTableConfig, + #[serde(default)] + pub accounts: HashMap, + pub l1_finalized: Option, + pub header: GenesisHeader, + #[serde(rename = "upgrade", with = "upgrade_serialization")] + #[serde(default)] + pub upgrades: BTreeMap, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +#[serde(rename_all = "snake_case")] +pub enum UpgradeType { + // Note: Wrapping this in a tuple variant causes deserialization to fail because + // the 'chain_config' name is also provided in the TOML input. + ChainConfig { chain_config: ChainConfig }, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Upgrade { + pub view: u64, + pub propose_window: u64, + #[serde(flatten)] + pub upgrade_type: UpgradeType, +} + +mod upgrade_serialization { + use crate::genesis::{Upgrade, UpgradeType}; + use serde::ser::SerializeSeq; + use serde::{ + de::{SeqAccess, Visitor}, + Deserialize, Deserializer, Serializer, + }; + use std::{collections::BTreeMap, fmt}; + use vbs::version::Version; + + pub fn serialize(map: &BTreeMap, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(map.len()))?; + for (version, upgrade) in map { + seq.serialize_element(&( + version.to_string(), + upgrade.view, + upgrade.propose_window, + upgrade.upgrade_type.clone(), + ))?; + } + seq.end() + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + struct VecToHashMap; + + impl<'de> Visitor<'de> for VecToHashMap { + type Value = BTreeMap; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a vector of tuples (key-value pairs)") + } + + fn visit_seq(self, mut seq: A) -> Result, A::Error> + where + A: SeqAccess<'de>, + { + let mut map = BTreeMap::new(); + + #[derive(Deserialize)] + struct UpgradeFields { + version: String, + view: u64, + propose_window: u64, + #[serde(flatten)] + upgrade_type: UpgradeType, + } + + while let Some(fields) = seq.next_element::()? { + // add try_from in Version + let version: Vec<_> = fields.version.split('.').collect(); + + let version = Version { + major: version[0].parse().expect("invalid version"), + minor: version[1].parse().expect("invalid version"), + }; + + map.insert( + version, + Upgrade { + view: fields.view, + propose_window: fields.propose_window, + upgrade_type: fields.upgrade_type, + }, + ); + } + + Ok(map) + } + } + + deserializer.deserialize_seq(VecToHashMap) + } +} + +impl Genesis { + pub fn to_file(&self, path: impl AsRef) -> anyhow::Result<()> { + let toml = toml::to_string_pretty(self)?; + std::fs::write(path, toml.as_bytes())?; + Ok(()) + } + + pub fn from_file(path: impl AsRef) -> anyhow::Result { + let path = path.as_ref(); + let bytes = std::fs::read(path).context(format!("genesis file {}", path.display()))?; + let text = std::str::from_utf8(&bytes).context("genesis file must be UTF-8")?; + + toml::from_str(text).context("malformed genesis file") + } +} + +#[cfg(test)] +mod test { + use super::*; + + use ethers::prelude::{Address, H160, H256}; + use toml::toml; + + #[test] + fn test_genesis_from_toml_with_optional_fields() { + let toml = toml! { + [stake_table] + capacity = 10 + + [chain_config] + chain_id = 12345 + max_block_size = 30000 + base_fee = 1 + fee_recipient = "0x0000000000000000000000000000000000000000" + fee_contract = "0x0000000000000000000000000000000000000000" + + [header] + timestamp = 123456 + + [accounts] + "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" = 100000 + "0x0000000000000000000000000000000000000000" = 42 + + [l1_finalized] + number = 64 + timestamp = "0x123def" + hash = "0x80f5dd11f2bdda2814cb1ad94ef30a47de02cf28ad68c89e104c00c4e51bb7a5" + + [[upgrade]] + version = "1.0" + view = 1 + propose_window = 10 + + [upgrade.chain_config] + chain_id = 12345 + max_block_size = 30000 + base_fee = 1 + fee_recipient = "0x0000000000000000000000000000000000000000" + fee_contract = "0x0000000000000000000000000000000000000000" + } + .to_string(); + + let genesis: Genesis = toml::from_str(&toml).unwrap_or_else(|err| panic!("{err:#}")); + assert_eq!(genesis.stake_table, StakeTableConfig { capacity: 10 }); + assert_eq!( + genesis.chain_config, + ChainConfig { + chain_id: 12345.into(), + max_block_size: 30000.into(), + base_fee: 1.into(), + fee_recipient: FeeAccount::default(), + fee_contract: Some(Address::default()) + } + ); + assert_eq!( + genesis.header, + GenesisHeader { + timestamp: Timestamp::from_integer(123456).unwrap(), + } + ); + assert_eq!( + genesis.accounts, + [ + ( + FeeAccount::from(H160([ + 0x23, 0x61, 0x8e, 0x81, 0xe3, 0xf5, 0xcd, 0xf7, 0xf5, 0x4c, 0x3d, 0x65, + 0xf7, 0xfb, 0xc0, 0xab, 0xf5, 0xb2, 0x1e, 0x8f + ])), + 100000.into() + ), + (FeeAccount::default(), 42.into()) + ] + .into_iter() + .collect::>() + ); + assert_eq!( + genesis.l1_finalized, + Some(L1Finalized::Block(L1BlockInfo { + number: 64, + timestamp: 0x123def.into(), + hash: H256([ + 0x80, 0xf5, 0xdd, 0x11, 0xf2, 0xbd, 0xda, 0x28, 0x14, 0xcb, 0x1a, 0xd9, 0x4e, + 0xf3, 0x0a, 0x47, 0xde, 0x02, 0xcf, 0x28, 0xad, 0x68, 0xc8, 0x9e, 0x10, 0x4c, + 0x00, 0xc4, 0xe5, 0x1b, 0xb7, 0xa5 + ]) + })) + ); + } + + #[test] + fn test_genesis_from_toml_without_optional_fields() { + let toml = toml! { + [stake_table] + capacity = 10 + + [chain_config] + chain_id = 12345 + max_block_size = 30000 + base_fee = 1 + fee_recipient = "0x0000000000000000000000000000000000000000" + + [header] + timestamp = 123456 + } + .to_string(); + + let genesis: Genesis = toml::from_str(&toml).unwrap_or_else(|err| panic!("{err:#}")); + assert_eq!(genesis.stake_table, StakeTableConfig { capacity: 10 }); + assert_eq!( + genesis.chain_config, + ChainConfig { + chain_id: 12345.into(), + max_block_size: 30000.into(), + base_fee: 1.into(), + fee_recipient: FeeAccount::default(), + fee_contract: None, + } + ); + assert_eq!( + genesis.header, + GenesisHeader { + timestamp: Timestamp::from_integer(123456).unwrap(), + } + ); + assert_eq!(genesis.accounts, HashMap::default()); + assert_eq!(genesis.l1_finalized, None); + } + + #[test] + fn test_genesis_l1_finalized_number_only() { + let toml = toml! { + [stake_table] + capacity = 10 + + [chain_config] + chain_id = 12345 + max_block_size = 30000 + base_fee = 1 + fee_recipient = "0x0000000000000000000000000000000000000000" + + [header] + timestamp = 123456 + + [l1_finalized] + number = 42 + } + .to_string(); + + let genesis: Genesis = toml::from_str(&toml).unwrap_or_else(|err| panic!("{err:#}")); + assert_eq!( + genesis.l1_finalized, + Some(L1Finalized::Number { number: 42 }) + ); + } + + #[test] + fn test_genesis_from_toml_units() { + let toml = toml! { + [stake_table] + capacity = 10 + + [chain_config] + chain_id = 12345 + max_block_size = "30mb" + base_fee = "1 gwei" + fee_recipient = "0x0000000000000000000000000000000000000000" + + [header] + timestamp = "2024-05-16T11:20:28-04:00" + } + .to_string(); + + let genesis: Genesis = toml::from_str(&toml).unwrap_or_else(|err| panic!("{err:#}")); + assert_eq!(genesis.stake_table, StakeTableConfig { capacity: 10 }); + assert_eq!(*genesis.chain_config.max_block_size, 30000000); + assert_eq!(genesis.chain_config.base_fee, 1_000_000_000.into()); + assert_eq!( + genesis.header, + GenesisHeader { + timestamp: Timestamp::from_integer(1715872828).unwrap(), + } + ) + } +} diff --git a/sequencer/src/header.rs b/sequencer/src/header.rs index a63826a6d..1ebb25b54 100644 --- a/sequencer/src/header.rs +++ b/sequencer/src/header.rs @@ -1,27 +1,31 @@ use crate::{ - block::{entry::TxTableEntryWord, tables::NameSpaceTable, NsTable}, + block::NsTable, chain_config::ResolvableChainConfig, + eth_signature_key::BuilderSignature, + genesis::UpgradeType, l1_client::L1Snapshot, - state::{BlockMerkleCommitment, FeeAccount, FeeInfo, FeeMerkleCommitment}, - ChainConfig, L1BlockInfo, Leaf, NodeState, SeqTypes, ValidatedState, + state::{BlockMerkleCommitment, FeeAccount, FeeAmount, FeeInfo, FeeMerkleCommitment}, + ChainConfig, L1BlockInfo, Leaf, NamespaceId, NodeState, SeqTypes, ValidatedState, }; -use anyhow::Context; +use anyhow::{ensure, Context}; use ark_serialize::CanonicalSerialize; use committable::{Commitment, Committable, RawCommitmentBuilder}; -use ethers::types; -use hotshot_query_service::availability::QueryableHeader; +use hotshot_query_service::{availability::QueryableHeader, explorer::ExplorerHeader, Resolvable}; use hotshot_types::{ traits::{ block_contents::{BlockHeader, BlockPayload, BuilderFee}, node_implementation::NodeType, - EncodeBytes, ValidatedState as HotShotState, + signature_key::BuilderSignatureKey, + ValidatedState as HotShotState, }, utils::BuilderCommitment, - vid::VidCommitment, + vid::{VidCommitment, VidCommon}, }; -use jf_primitives::merkle_tree::prelude::*; +use jf_merkle_tree::prelude::*; use serde::{Deserialize, Serialize}; +use snafu::Snafu; use time::OffsetDateTime; +use vbs::version::Version; /// A header is like a [`Block`] with the body replaced by a digest. #[derive(Clone, Debug, Deserialize, Serialize, Hash, PartialEq, Eq)] @@ -75,14 +79,22 @@ pub struct Header { pub payload_commitment: VidCommitment, pub builder_commitment: BuilderCommitment, - pub ns_table: NameSpaceTable, + pub ns_table: NsTable, /// Root Commitment of Block Merkle Tree pub block_merkle_tree_root: BlockMerkleCommitment, /// Root Commitment of `FeeMerkleTree` pub fee_merkle_tree_root: FeeMerkleCommitment, - /// Account (etheruem address) of builder - pub builder_signature: Option, + /// Fee paid by the block builder pub fee_info: FeeInfo, + /// Account (etheruem address) of builder + /// + /// This signature is not considered formally part of the header; it is just evidence proving + /// that other parts of the header (`fee_info`) are correct. It exists in the header so that it + /// is available to all nodes to be used during validation. But since it is checked during + /// consensus, any downstream client who has a proof of consensus finality of a header can trust + /// that `fee_info` is correct without relying on the signature. Thus, this signature is not + /// included in the header commitment. + pub builder_signature: Option, } impl Committable for Header { @@ -120,18 +132,6 @@ impl Committable for Header { } } -impl Committable for NameSpaceTable { - fn commit(&self) -> Commitment { - RawCommitmentBuilder::new(&Self::tag()) - .var_size_bytes(self.get_bytes()) - .finalize() - } - - fn tag() -> String { - "NSTABLE".into() - } -} - impl Header { #[allow(clippy::too_many_arguments)] fn from_info( @@ -141,14 +141,13 @@ impl Header { parent_leaf: &Leaf, mut l1: L1Snapshot, l1_deposits: &[FeeInfo], - builder_fee: FeeInfo, - builder_signature: types::Signature, + builder_fee: BuilderFee, mut timestamp: u64, mut state: ValidatedState, chain_config: ChainConfig, ) -> anyhow::Result { // Increment height. - let parent_header = parent_leaf.get_block_header(); + let parent_header = parent_leaf.block_header(); let height = parent_header.height + 1; // Ensure the timestamp does not decrease. We can trust `parent.timestamp` because `parent` @@ -205,9 +204,20 @@ impl Header { } // Charge the builder fee. + ensure!( + builder_fee.fee_account.validate_fee_signature( + &builder_fee.fee_signature, + builder_fee.fee_amount, + &ns_table, + &payload_commitment, + ), + "invalid builder signature" + ); + let builder_signature = Some(builder_fee.fee_signature); + let fee_info = builder_fee.into(); state - .charge_fee(builder_fee) - .context(format!("invalid builder fee {builder_fee:?}"))?; + .charge_fee(fee_info, chain_config.fee_recipient) + .context(format!("invalid builder fee {fee_info:?}"))?; let fee_merkle_tree_root = state.fee_merkle_tree.commitment(); Ok(Self { @@ -221,67 +231,110 @@ impl Header { ns_table, fee_merkle_tree_root, block_merkle_tree_root, - fee_info: builder_fee, - builder_signature: Some(builder_signature), + fee_info, + builder_signature, }) } - /// Message authorizing a fee payment for inclusion of a certain payload. - /// - /// This message relates the fee info in this header to the payload corresponding to the header. - /// The message is signed by the builder (or whoever is paying for inclusion of the block) and - /// validated by consensus, as authentication for charging the fee to the builder account. - pub fn fee_message(&self) -> anyhow::Result> { - Ok(fee_message( - self.fee_info.amount().as_u64().context(format!( - "fee amount out of range: {:?}", - self.fee_info.amount() - ))?, - self.payload_commitment, - self.metadata(), - )) + async fn get_chain_config( + validated_state: &ValidatedState, + instance_state: &NodeState, + ) -> ChainConfig { + let validated_cf = validated_state.chain_config; + let instance_cf = instance_state.chain_config; + + if validated_cf.commit() == instance_cf.commitment() { + return instance_cf; + } + + match validated_cf.resolve() { + Some(cf) => cf, + None => { + tracing::info!("fetching chain config {} from peers", validated_cf.commit()); + + instance_state + .peers + .as_ref() + .fetch_chain_config(validated_cf.commit()) + .await + } + } + } +} + +#[derive(Debug, Snafu)] +#[snafu(display("Invalid Block Header {msg}"))] +pub struct InvalidBlockHeader { + msg: String, +} +impl InvalidBlockHeader { + fn new(msg: String) -> Self { + Self { msg } + } +} + +impl From for InvalidBlockHeader { + fn from(err: anyhow::Error) -> Self { + Self::new(format!("{err:#}")) } } impl BlockHeader for Header { + type Error = InvalidBlockHeader; + #[tracing::instrument( skip_all, - fields(view = ?parent_leaf.get_view_number(), height = parent_leaf.get_block_header().height), + fields( + node_id = instance_state.node_id, + view = ?parent_leaf.view_number(), + height = parent_leaf.block_header().height, + ), )] + async fn new( parent_state: &ValidatedState, instance_state: &NodeState, parent_leaf: &Leaf, payload_commitment: VidCommitment, builder_commitment: BuilderCommitment, - metadata: <::BlockPayload as BlockPayload>::Metadata, + metadata: <::BlockPayload as BlockPayload>::Metadata, builder_fee: BuilderFee, - ) -> Self { + _vid_common: VidCommon, + version: Version, + ) -> Result { + let height = parent_leaf.height(); + let view = parent_leaf.view_number(); + let mut validated_state = parent_state.clone(); - // Validate the builder's signature, recovering their fee account address so that we can - // fetch the account state if missing. - let fee_msg = fee_message(builder_fee.fee_amount, payload_commitment, &metadata); - let builder_account = FeeAccount::from( - builder_fee - .fee_signature - .recover(fee_msg) - .expect("invalid builder signature"), - ); + let chain_config = if version > instance_state.current_version { + match instance_state + .upgrades + .get(&version) + .map(|upgrade| match upgrade.upgrade_type { + UpgradeType::ChainConfig { chain_config } => chain_config, + }) { + Some(cf) => cf, + None => Header::get_chain_config(&validated_state, instance_state).await, + } + } else { + Header::get_chain_config(&validated_state, instance_state).await + }; - // Figure out which accounts we need to fetch, in case we are missing some state needed to - // construct the header. - let accounts = std::iter::once(builder_account); + validated_state.chain_config = chain_config.into(); // Fetch the latest L1 snapshot. - let l1_snapshot = instance_state.l1_client().snapshot().await; + let l1_snapshot = instance_state.l1_client.snapshot().await; // Fetch the new L1 deposits between parent and current finalized L1 block. - let l1_deposits = if let Some(block_info) = l1_snapshot.finalized { + let l1_deposits = if let (Some(addr), Some(block_info)) = + (chain_config.fee_contract, l1_snapshot.finalized) + { instance_state .l1_client .get_finalized_deposits( + addr, parent_leaf - .get_block_header() + .block_header() .l1_finalized .map(|block_info| block_info.number), block_info.number, @@ -290,72 +343,78 @@ impl BlockHeader for Header { } else { vec![] }; - // Find missing fee state entries - let missing_accounts = parent_state - .forgotten_accounts(accounts.chain(l1_deposits.iter().map(|info| info.account()))); + // Find missing fee state entries. We will need to use the builder account which is paying a + // fee and the recipient account which is receiving it, plus any counts receiving deposits + // in this block. + let missing_accounts = parent_state.forgotten_accounts( + [builder_fee.fee_account, chain_config.fee_recipient] + .into_iter() + .chain(l1_deposits.iter().map(|info| info.account())), + ); if !missing_accounts.is_empty() { - tracing::warn!("fetching missing accounts {missing_accounts:?} from peers"); + tracing::warn!( + height, + ?view, + ?missing_accounts, + "fetching missing accounts from peers" + ); // Fetch missing fee state entries - // Unwrapping here is okay as we retry until we get the accounts or until the task is canceled. let missing_account_proofs = instance_state .peers .as_ref() .fetch_accounts( - parent_leaf.get_view_number(), + height, + view, parent_state.fee_merkle_tree.commitment(), missing_accounts, ) - .await - .unwrap(); + .await?; // Insert missing fee state entries for account in missing_account_proofs.iter() { account .proof .remember(&mut validated_state.fee_merkle_tree) - .expect("proof previously verified"); + .context("remembering fee account")?; } } // Ensure merkle tree has frontier if validated_state.need_to_fetch_blocks_mt_frontier() { - let view = parent_leaf.get_view_number(); - tracing::warn!("fetching block frontier for view {view:?} from peers"); + tracing::warn!(height, ?view, "fetching block frontier from peers"); instance_state .peers .as_ref() - .remember_blocks_merkle_tree(view, &mut validated_state.block_merkle_tree) + .remember_blocks_merkle_tree(height, view, &mut validated_state.block_merkle_tree) .await - .expect("failed to remember proof"); + .context("remembering block proof")?; } - Self::from_info( + Ok(Self::from_info( payload_commitment, builder_commitment, metadata, parent_leaf, l1_snapshot, &l1_deposits, - FeeInfo::new(builder_account, builder_fee.fee_amount), - builder_fee.fee_signature, + builder_fee, OffsetDateTime::now_utc().unix_timestamp() as u64, validated_state, - instance_state.chain_config, - ) - // TODO we should be able to return an error from `Header::new` - .unwrap_or_else(|err| panic!("invalid proposal: {err:#}")) + chain_config, + )?) } fn genesis( instance_state: &NodeState, payload_commitment: VidCommitment, builder_commitment: BuilderCommitment, - ns_table: <::BlockPayload as BlockPayload>::Metadata, + ns_table: <::BlockPayload as BlockPayload>::Metadata, ) -> Self { let ValidatedState { fee_merkle_tree, block_merkle_tree, + .. } = ValidatedState::genesis(instance_state).0; let block_merkle_tree_root = block_merkle_tree.commitment(); let fee_merkle_tree_root = fee_merkle_tree.commitment(); @@ -365,9 +424,13 @@ impl BlockHeader for Header { // timestamps or L1 values. chain_config: instance_state.chain_config.into(), height: 0, - timestamp: 0, - l1_head: 0, - l1_finalized: None, + timestamp: instance_state.genesis_header.timestamp.unix_timestamp(), + l1_finalized: instance_state.l1_genesis, + // Make sure the L1 head is not behind the finalized block. + l1_head: instance_state + .l1_genesis + .map(|block| block.number) + .unwrap_or_default(), payload_commitment, builder_commitment, ns_table, @@ -386,7 +449,9 @@ impl BlockHeader for Header { self.payload_commitment } - fn metadata(&self) -> &<::BlockPayload as BlockPayload>::Metadata { + fn metadata( + &self, + ) -> &<::BlockPayload as BlockPayload>::Metadata { &self.ns_table } @@ -396,24 +461,47 @@ impl BlockHeader for Header { } } -fn fee_message( - amount: u64, - payload_commitment: VidCommitment, - metadata: &<::BlockPayload as BlockPayload>::Metadata, -) -> Vec { - let mut data = vec![]; - data.extend_from_slice(amount.to_be_bytes().as_ref()); - data.extend_from_slice(metadata.encode().as_ref()); - data.extend_from_slice(payload_commitment.as_ref()); - data -} - impl QueryableHeader for Header { fn timestamp(&self) -> u64 { self.timestamp } } +impl ExplorerHeader for Header { + type BalanceAmount = FeeAmount; + type WalletAddress = FeeAccount; + type ProposerId = FeeAccount; + type NamespaceId = NamespaceId; + + fn proposer_id(&self) -> Self::ProposerId { + self.fee_info.account() + } + + fn fee_info_account(&self) -> Self::WalletAddress { + self.fee_info.account() + } + + fn fee_info_balance(&self) -> Self::BalanceAmount { + self.fee_info.amount() + } + + /// reward_balance at the moment is only implemented as a stub, as block + /// rewards have not yet been implemented. + /// + /// TODO: update implementation when rewards have been created / supported. + /// Issue: https://github.com/EspressoSystems/espresso-sequencer/issues/1453 + fn reward_balance(&self) -> Self::BalanceAmount { + FeeAmount::from(0) + } + + fn namespace_ids(&self) -> Vec { + self.ns_table + .iter() + .map(|i| self.ns_table.read_ns_id_unchecked(&i)) + .collect() + } +} + #[cfg(test)] mod test_headers { use std::sync::Arc; @@ -424,8 +512,7 @@ mod test_headers { eth_signature_key::EthKeyPair, l1_client::L1Client, state::{ - apply_proposal, get_l1_deposits, validate_proposal, BlockMerkleTree, Delta, - FeeMerkleTree, + validate_proposal, BlockMerkleTree, FeeAccount, FeeMerkleTree, ProposalValidationError, }, NodeState, }; @@ -434,7 +521,9 @@ mod test_headers { types::{Address, U256}, utils::Anvil, }; - use hotshot_types::traits::signature_key::BuilderSignatureKey; + use hotshot_types::{traits::signature_key::BuilderSignatureKey, vid::vid_scheme}; + use jf_vid::VidScheme; + use vbs::version::{StaticVersion, StaticVersionType}; #[derive(Debug, Default)] #[must_use] @@ -457,7 +546,7 @@ mod test_headers { } impl TestCase { - fn run(self) { + async fn run(self) { setup_logging(); setup_backtrace(); @@ -466,14 +555,14 @@ mod test_headers { assert!(self.expected_l1_head >= self.parent_l1_head); assert!(self.expected_l1_finalized >= self.parent_l1_finalized); - let genesis = GenesisForTest::default(); + let genesis = GenesisForTest::default().await; let mut parent = genesis.header.clone(); parent.timestamp = self.parent_timestamp; parent.l1_head = self.parent_l1_head; parent.l1_finalized = self.parent_l1_finalized; let mut parent_leaf = genesis.leaf.clone(); - *parent_leaf.get_block_header_mut() = parent.clone(); + *parent_leaf.block_header_mut() = parent.clone(); let block_merkle_tree = BlockMerkleTree::from_elems(Some(32), Vec::>::new()).unwrap(); @@ -487,6 +576,7 @@ mod test_headers { let mut validated_state = ValidatedState { block_merkle_tree: block_merkle_tree.clone(), fee_merkle_tree, + chain_config: genesis.instance_state.chain_config.into(), }; let (fee_account, fee_key) = FeeAccount::generated_from_seed_indexed([0; 32], 0); @@ -509,8 +599,11 @@ mod test_headers { finalized: self.l1_finalized, }, &self.l1_deposits, - FeeInfo::new(fee_account, fee_amount), - fee_signature, + BuilderFee { + fee_account, + fee_amount, + fee_signature, + }, self.timestamp, validated_state.clone(), genesis.instance_state.chain_config, @@ -544,24 +637,25 @@ mod test_headers { } } - #[test] - fn test_new_header() { + #[async_std::test] + async fn test_new_header() { // Simplest case: building on genesis, L1 info and timestamp unchanged. - TestCase::default().run() + TestCase::default().run().await } - #[test] - fn test_new_header_advance_timestamp() { + #[async_std::test] + async fn test_new_header_advance_timestamp() { TestCase { timestamp: 1, expected_timestamp: 1, ..Default::default() } .run() + .await } - #[test] - fn test_new_header_advance_l1_block() { + #[async_std::test] + async fn test_new_header_advance_l1_block() { TestCase { parent_l1_head: 0, parent_l1_finalized: Some(l1_block(0)), @@ -575,20 +669,22 @@ mod test_headers { ..Default::default() } .run() + .await } - #[test] - fn test_new_header_advance_l1_finalized_from_none() { + #[async_std::test] + async fn test_new_header_advance_l1_finalized_from_none() { TestCase { l1_finalized: Some(l1_block(1)), expected_l1_finalized: Some(l1_block(1)), ..Default::default() } .run() + .await } - #[test] - fn test_new_header_timestamp_behind_finalized_l1_block() { + #[async_std::test] + async fn test_new_header_timestamp_behind_finalized_l1_block() { let l1_finalized = Some(L1BlockInfo { number: 1, timestamp: 1.into(), @@ -606,10 +702,11 @@ mod test_headers { ..Default::default() } .run() + .await } - #[test] - fn test_new_header_timestamp_behind() { + #[async_std::test] + async fn test_new_header_timestamp_behind() { TestCase { parent_timestamp: 1, timestamp: 0, @@ -618,10 +715,11 @@ mod test_headers { ..Default::default() } .run() + .await } - #[test] - fn test_new_header_l1_head_behind() { + #[async_std::test] + async fn test_new_header_l1_head_behind() { TestCase { parent_l1_head: 1, l1_head: 0, @@ -630,10 +728,11 @@ mod test_headers { ..Default::default() } .run() + .await } - #[test] - fn test_new_header_l1_finalized_behind_some() { + #[async_std::test] + async fn test_new_header_l1_finalized_behind_some() { TestCase { parent_l1_finalized: Some(l1_block(1)), l1_finalized: Some(l1_block(0)), @@ -642,10 +741,11 @@ mod test_headers { ..Default::default() } .run() + .await } - #[test] - fn test_new_header_l1_finalized_behind_none() { + #[async_std::test] + async fn test_new_header_l1_finalized_behind_none() { TestCase { parent_l1_finalized: Some(l1_block(0)), l1_finalized: None, @@ -654,19 +754,21 @@ mod test_headers { ..Default::default() } .run() + .await } - #[test] - fn test_new_header_deposits_one() { + #[async_std::test] + async fn test_new_header_deposits_one() { TestCase { l1_deposits: vec![FeeInfo::new(Address::default(), 1)], ..Default::default() } .run() + .await } - #[test] - fn test_new_header_deposits_many() { + #[async_std::test] + async fn test_new_header_deposits_many() { TestCase { l1_deposits: [ (Address::default(), 1), @@ -679,6 +781,7 @@ mod test_headers { ..Default::default() } .run() + .await } struct GenesisForTest { @@ -686,16 +789,16 @@ mod test_headers { pub validated_state: ValidatedState, pub leaf: Leaf, pub header: Header, - pub ns_table: NameSpaceTable, + pub ns_table: NsTable, } - impl Default for GenesisForTest { - fn default() -> Self { + impl GenesisForTest { + async fn default() -> Self { let instance_state = NodeState::mock(); let validated_state = ValidatedState::genesis(&instance_state).0; - let leaf = Leaf::genesis(&instance_state); - let header = leaf.get_block_header().clone(); - let ns_table = leaf.get_block_payload().unwrap().get_ns_table().clone(); + let leaf = Leaf::genesis(&validated_state, &instance_state).await; + let header = leaf.block_header().clone(); + let ns_table = leaf.block_payload().unwrap().ns_table().clone(); Self { instance_state, validated_state, @@ -706,16 +809,17 @@ mod test_headers { } } - #[test] - fn test_validate_proposal_error_cases() { - let genesis = GenesisForTest::default(); + #[async_std::test] + async fn test_validate_proposal_error_cases() { + let genesis = GenesisForTest::default().await; + let vid_common = vid_scheme(1).disperse([]).unwrap().common; let mut validated_state = ValidatedState::default(); let mut block_merkle_tree = validated_state.block_merkle_tree.clone(); let mut parent_header = genesis.header.clone(); let mut parent_leaf = genesis.leaf.clone(); - *parent_leaf.get_block_header_mut() = parent_header.clone(); + *parent_leaf.block_header_mut() = parent_header.clone(); // Populate the tree with an initial `push`. block_merkle_tree.push(genesis.header.commit()).unwrap(); @@ -724,49 +828,78 @@ mod test_headers { parent_header.block_merkle_tree_root = block_merkle_tree_root; let mut proposal = parent_header.clone(); - let mut delta = Delta::default(); + let ver = StaticVersion::<1, 0>::version(); + // Pass a different chain config to trigger a chain config validation error. - let state = apply_proposal(&validated_state, &mut delta, &parent_leaf, vec![]); + let state = validated_state + .apply_header(&genesis.instance_state, &parent_leaf, &proposal, ver) + .await + .unwrap() + .0; - let result = validate_proposal( - &state, - ChainConfig::new(U256::zero(), 0u64, U256::zero()), - &parent_leaf, - &proposal, - ) - .unwrap_err(); + let chain_config = ChainConfig { + chain_id: U256::zero().into(), + ..Default::default() + }; + let err = validate_proposal(&state, chain_config, &parent_leaf, &proposal, &vid_common) + .unwrap_err(); - assert!(format!("{}", result.root_cause()).starts_with("Invalid Chain Config:")); + assert_eq!( + ProposalValidationError::InvalidChainConfig { + expected: format!("{:?}", chain_config), + proposal: format!("{:?}", proposal.chain_config) + }, + err + ); // Advance `proposal.height` to trigger validation error. - let validated_state = apply_proposal(&validated_state, &mut delta, &parent_leaf, vec![]); - let result = validate_proposal( + let validated_state = validated_state + .apply_header(&genesis.instance_state, &parent_leaf, &proposal, ver) + .await + .unwrap() + .0; + let err = validate_proposal( &validated_state, genesis.instance_state.chain_config, &parent_leaf, &proposal, + &vid_common, ) .unwrap_err(); assert_eq!( - format!("{}", result.root_cause()), - "Invalid Height Error: 0, 0" + ProposalValidationError::InvalidHeight { + parent_height: 0, + proposal_height: 0 + }, + err ); // proposed `Header` root should include parent + parent.commit proposal.height += 1; - let validated_state = apply_proposal(&validated_state, &mut delta, &parent_leaf, vec![]); + let validated_state = validated_state + .apply_header(&genesis.instance_state, &parent_leaf, &proposal, ver) + .await + .unwrap() + .0; - let result = validate_proposal( + let err = validate_proposal( &validated_state, genesis.instance_state.chain_config, &parent_leaf, &proposal, + &vid_common, ) .unwrap_err(); // Fails b/c `proposal` has not advanced from `parent` - assert!(format!("{}", result.root_cause()).contains("Invalid Block Root Error")); + assert_eq!( + ProposalValidationError::InvalidBlockRoot { + expected_root: validated_state.block_merkle_tree.commitment(), + proposal_root: proposal.block_merkle_tree_root + }, + err + ); } #[async_std::test] @@ -775,12 +908,11 @@ mod test_headers { setup_backtrace(); let anvil = Anvil::new().block_time(1u32).spawn(); - let mut genesis_state = NodeState::mock().with_l1(L1Client::new( - anvil.endpoint().parse().unwrap(), - Address::default(), - )); + let mut genesis_state = + NodeState::mock().with_l1(L1Client::new(anvil.endpoint().parse().unwrap(), 1)); - let genesis = GenesisForTest::default(); + let genesis = GenesisForTest::default().await; + let vid_common = vid_scheme(1).disperse([]).unwrap().common; let mut parent_state = genesis.validated_state.clone(); @@ -799,12 +931,12 @@ mod test_headers { parent_header.fee_merkle_tree_root = fee_merkle_tree_root; let mut parent_leaf = genesis.leaf.clone(); - *parent_leaf.get_block_header_mut() = parent_header.clone(); + *parent_leaf.block_header_mut() = parent_header.clone(); // Forget the state to trigger lookups in Header::new let forgotten_state = parent_state.forget(); genesis_state.peers = Arc::new(MockStateCatchup::from_iter([( - parent_leaf.get_view_number(), + parent_leaf.view_number(), Arc::new(parent_state.clone()), )])); // Get a proposal from a parent @@ -820,6 +952,7 @@ mod test_headers { FeeAccount::sign_fee(&key_pair, fee_amount, &ns_table, &payload_commitment).unwrap(); let builder_fee = BuilderFee { fee_amount, + fee_account: key_pair.fee_account(), fee_signature, }; let proposal = Header::new( @@ -830,13 +963,16 @@ mod test_headers { builder_commitment, ns_table, builder_fee, + vid_common.clone(), + hotshot_types::constants::Base::VERSION, ) - .await; + .await + .unwrap(); let mut proposal_state = parent_state.clone(); for fee_info in genesis_state .l1_client - .get_finalized_deposits(None, 0) + .get_finalized_deposits(Address::default(), None, 0) .await { proposal_state.insert_fee_deposit(fee_info).unwrap(); @@ -845,16 +981,22 @@ mod test_headers { let mut block_merkle_tree = proposal_state.block_merkle_tree.clone(); block_merkle_tree.push(proposal.commit()).unwrap(); - let l1_deposits = get_l1_deposits(&genesis_state, &proposal, &parent_leaf).await; - - let mut delta = Delta::default(); - - let proposal_state = apply_proposal(&proposal_state, &mut delta, &parent_leaf, l1_deposits); + let proposal_state = proposal_state + .apply_header( + &genesis_state, + &parent_leaf, + &proposal, + StaticVersion::<1, 0>::version(), + ) + .await + .unwrap() + .0; validate_proposal( &proposal_state, genesis.instance_state.chain_config, &parent_leaf, &proposal.clone(), + &vid_common, ) .unwrap(); diff --git a/sequencer/src/hotshot_commitment.rs b/sequencer/src/hotshot_commitment.rs index c275323fa..cf4759d25 100644 --- a/sequencer/src/hotshot_commitment.rs +++ b/sequencer/src/hotshot_commitment.rs @@ -275,7 +275,7 @@ fn build_sequence_batches_txn( #[cfg(test)] mod test { use super::*; - use crate::{l1_client::L1Client, Leaf, NodeState}; + use crate::{l1_client::L1Client, Leaf, NodeState, ValidatedState}; use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; use async_std::task::spawn; use committable::Committable; @@ -320,17 +320,17 @@ mod test { .flatten() .ok_or_else(|| { Self::Error::catch_all( - StatusCode::NotFound, + StatusCode::NOT_FOUND, format!("no leaf for height {height}"), ) }) } } - fn mock_leaf(height: u64, node_state: &NodeState) -> LeafQueryData { - let mut leaf = Leaf::genesis(node_state); - let mut qc = QuorumCertificate::genesis(node_state); - leaf.get_block_header_mut().height = height; + async fn mock_leaf(height: u64, node_state: &NodeState) -> LeafQueryData { + let mut leaf = Leaf::genesis(&ValidatedState::default(), node_state).await; + let mut qc = QuorumCertificate::genesis(&ValidatedState::default(), node_state).await; + leaf.block_header_mut().height = height; qc.data.leaf_commit = leaf.commit(); LeafQueryData::new(leaf, qc).unwrap() } @@ -375,13 +375,12 @@ mod test { let num_batches = l1.hotshot.max_blocks().call().await.unwrap().as_usize(); let mut data = MockDataSource::default(); - let node_state = NodeState::mock().with_l1(L1Client::new( - anvil.provider().url().clone(), - Address::default(), - )); + let node_state = + NodeState::mock().with_l1(L1Client::new(anvil.provider().url().clone(), 1)); for i in 0..num_batches { - data.leaves.push(Some(mock_leaf(i as u64, &node_state))); + data.leaves + .push(Some(mock_leaf(i as u64, &node_state).await)); } tracing::info!("sequencing batches: {:?}", data.leaves); @@ -447,11 +446,9 @@ mod test { // Create a test batch. let mut data = MockDataSource::default(); - let node_state = NodeState::mock().with_l1(L1Client::new( - anvil.provider().url().clone(), - Address::default(), - )); - data.leaves.push(Some(mock_leaf(0, &node_state))); + let node_state = + NodeState::mock().with_l1(L1Client::new(anvil.provider().url().clone(), 1)); + data.leaves.push(Some(mock_leaf(0, &node_state).await)); // Connect to the HotShot contract with the expected L1 client. let hotshot = HotShot::new(l1.hotshot.address(), adaptor_l1_signer); @@ -476,7 +473,7 @@ mod test { assert_eq!(l1.hotshot.block_height().call().await.unwrap().as_u64(), 1); // Once a new batch is available, we can sequence it. - data.leaves.push(Some(mock_leaf(1, &node_state))); + data.leaves.push(Some(mock_leaf(1, &node_state).await)); sync_with_l1(1, &data, &hotshot).await.unwrap(); let (event, _) = wait_for_new_batches(&l1, from_block.as_u64()).await; assert_eq!(event.first_block_number.as_u64(), 1); @@ -511,15 +508,13 @@ mod test { .unwrap(), ); - let node_state = NodeState::mock().with_l1(L1Client::new( - anvil.provider().url().clone(), - Address::default(), - )); + let node_state = + NodeState::mock().with_l1(L1Client::new(anvil.provider().url().clone(), 1)); // Create a sequence of leaves, some of which are missing. let mut data = MockDataSource::default(); data.leaves - .extend([None, Some(mock_leaf(1, &node_state)), None]); + .extend([None, Some(mock_leaf(1, &node_state).await), None]); // Connect to the HotShot contract with the expected L1 client. let hotshot = HotShot::new(l1.hotshot.address(), adaptor_l1_signer); @@ -530,7 +525,7 @@ mod test { // If the first leaf is present but subsequent leaves are missing, we should sequence the // leaves that are available. - data.leaves[0] = Some(mock_leaf(0, &node_state)); + data.leaves[0] = Some(mock_leaf(0, &node_state).await); sync_with_l1(3, &data, &hotshot).await.unwrap(); // Check the NewBatches event. diff --git a/sequencer/src/l1_client.rs b/sequencer/src/l1_client.rs index e2b10973d..f88d7a544 100644 --- a/sequencer/src/l1_client.rs +++ b/sequencer/src/l1_client.rs @@ -19,10 +19,18 @@ use crate::state::FeeInfo; use async_std::task::sleep; use committable::{Commitment, Committable, RawCommitmentBuilder}; +use contract_bindings::fee_contract::FeeContract; use ethers::prelude::*; -use futures::join; +use futures::{ + join, + stream::{self, StreamExt}, +}; use serde::{Deserialize, Serialize}; -use std::{cmp::Ordering, sync::Arc, time::Duration}; +use std::{ + cmp::{min, Ordering}, + sync::Arc, + time::Duration, +}; use url::Url; #[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Hash, PartialEq, Eq)] @@ -90,18 +98,19 @@ impl Committable for L1BlockInfo { pub struct L1Client { retry_delay: Duration, /// `Provider` from `ethers-provider`. - provider: Provider, - /// `Address` of fee contract. - _address: Address, + provider: Arc>, + /// Maximum number of L1 blocks that can be scanned for events in a single query. + events_max_block_range: u64, } impl L1Client { /// Instantiate an `L1Client` for a given `Url`. - pub fn new(url: Url, contract_address: Address) -> Self { + pub fn new(url: Url, events_max_block_range: u64) -> Self { + let provider = Arc::new(Provider::new(Http::new(url))); Self { retry_delay: Duration::from_secs(1), - provider: Provider::new(Http::new(url)), - _address: contract_address, + provider, + events_max_block_range, } } /// Get a snapshot from the l1. @@ -109,6 +118,62 @@ impl L1Client { let (head, finalized) = join!(self.get_block_number(), self.get_finalized_block()); L1Snapshot { head, finalized } } + + /// Get information about the given block. + /// + /// If the desired block number is not finalized yet, this function will block until it becomes + /// finalized. + pub async fn wait_for_finalized_block(&self, number: u64) -> L1BlockInfo { + let interval = self.provider.get_interval(); + + // Wait for the block to finalize. + let finalized = loop { + let Some(block) = self.get_finalized_block().await else { + tracing::info!("waiting for finalized block"); + sleep(interval).await; + continue; + }; + if block.number >= number { + break block; + } + tracing::info!(current_finalized = %block.number, "waiting for finalized block"); + sleep(interval).await; + continue; + }; + + if finalized.number == number { + return finalized; + } + + // The finalized block may have skipped over the block of interest. In this case, our block + // is still finalized, since it is before the finalized block. We just need to fetch it. + loop { + let block = match self.provider.get_block(number).await { + Ok(Some(block)) => block, + Ok(None) => { + tracing::error!(number, "no such block"); + sleep(interval).await; + continue; + } + Err(err) => { + tracing::error!(%err, number, "failed to get L1 block"); + sleep(interval).await; + continue; + } + }; + let Some(hash) = block.hash else { + tracing::error!(number, ?block, "L1 block has no hash"); + sleep(interval).await; + continue; + }; + break L1BlockInfo { + number, + hash, + timestamp: block.timestamp, + }; + } + } + /// Proxy to `Provider.get_block_number`. async fn get_block_number(&self) -> u64 { loop { @@ -137,12 +202,13 @@ impl L1Client { /// and `new`. Returns `Vec` pub async fn get_finalized_deposits( &self, + fee_contract_address: Address, prev_finalized: Option, new_finalized: u64, ) -> Vec { // No new blocks have been finalized, therefore there are no // new deposits. - if prev_finalized == Some(new_finalized) { + if prev_finalized >= Some(new_finalized) { return vec![]; } @@ -150,27 +216,49 @@ impl L1Client { // haven't processed *any* blocks yet. let prev = prev_finalized.map(|prev| prev + 1).unwrap_or(0); - // query for deposit events, loop until successful. - let events = loop { - match contract_bindings::fee_contract::FeeContract::new( - self._address, - Arc::new(&self.provider), - ) - .deposit_filter() - .address(self._address.into()) - .from_block(prev) - .to_block(new_finalized) - .query() - .await - { - Ok(events) => break events, - Err(e) => { - tracing::warn!("Fee Event Error: {}", e); - sleep(self.retry_delay).await; + // Divide the range `prev_finalized..=new_finalized` into chunks of size + // `events_max_block_range`. + let mut start = prev; + let end = new_finalized; + let chunk_size = self.events_max_block_range; + let chunks = std::iter::from_fn(move || { + let chunk_end = min(start + chunk_size - 1, end); + if chunk_end < start { + return None; + } + + let chunk = (start, chunk_end); + start = chunk_end + 1; + Some(chunk) + }); + + // Fetch events for each chunk. + let events = stream::iter(chunks).then(|(from, to)| { + let retry_delay = self.retry_delay; + let fee_contract = FeeContract::new(fee_contract_address, self.provider.clone()); + async move { + tracing::debug!(from, to, "fetch events in range"); + + // query for deposit events, loop until successful. + loop { + match fee_contract + .deposit_filter() + .address(fee_contract.address().into()) + .from_block(from) + .to_block(to) + .query() + .await + { + Ok(events) => break stream::iter(events), + Err(err) => { + tracing::warn!(from, to, %err, "Fee Event Error"); + sleep(retry_delay).await; + } + } } } - }; - events.into_iter().map(Into::into).collect() + }); + events.flatten().map(FeeInfo::from).collect().await } } @@ -206,19 +294,22 @@ async fn get_finalized_block( #[cfg(test)] mod test { - use std::ops::Add; - use super::*; use crate::NodeState; + use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; use contract_bindings::fee_contract::FeeContract; use ethers::utils::{parse_ether, Anvil}; + use std::ops::Add; #[async_std::test] async fn test_l1_block_fetching() -> anyhow::Result<()> { + setup_logging(); + setup_backtrace(); + // Test l1_client methods against `ethers::Provider`. There is // also some sanity testing demonstrating `Anvil` availability. let anvil = Anvil::new().block_time(1u32).spawn(); - let l1_client = L1Client::new(anvil.endpoint().parse().unwrap(), Address::default()); + let l1_client = L1Client::new(anvil.endpoint().parse().unwrap(), 1); let provider = &l1_client.provider; let version = provider.client_version().await.unwrap(); @@ -226,11 +317,8 @@ mod test { // Test that nothing funky is happening to the provider when // passed along in state. - let state = NodeState::mock().with_l1(L1Client::new( - anvil.endpoint().parse().unwrap(), - Address::default(), - )); - let version = state.l1_client().provider.client_version().await.unwrap(); + let state = NodeState::mock().with_l1(L1Client::new(anvil.endpoint().parse().unwrap(), 1)); + let version = state.l1_client.provider.client_version().await.unwrap(); assert_eq!("anvil/v0.2.0", version); // compare response of underlying provider w/ `get_block_number` @@ -253,13 +341,16 @@ mod test { #[async_std::test] async fn test_get_finalized_deposits() -> anyhow::Result<()> { + setup_logging(); + setup_backtrace(); + // how many deposits will we make let deposits = 5; let deploy_txn_count = 2; let anvil = Anvil::new().spawn(); let wallet_address = anvil.addresses().first().cloned().unwrap(); - let l1_client = L1Client::new(anvil.endpoint().parse().unwrap(), Address::default()); + let l1_client = L1Client::new(anvil.endpoint().parse().unwrap(), 1); let wallet: LocalWallet = anvil.keys()[0].clone().into(); // In order to deposit we need a provider that can sign. @@ -324,18 +415,19 @@ mod test { assert_eq!(deposits + deploy_txn_count, head); // Use non-signing `L1Client` to retrieve data. - let l1_client = L1Client::new( - anvil.endpoint().parse().unwrap(), - fee_contract_proxy.address(), - ); + let l1_client = L1Client::new(anvil.endpoint().parse().unwrap(), 1); // Set prev deposits to `None` so `Filter` will start at block // 0. The test would also succeed if we pass `0` (b/c first // block did not deposit). let pending = l1_client - .get_finalized_deposits(None, deposits + deploy_txn_count) + .get_finalized_deposits( + fee_contract_proxy.address(), + None, + deposits + deploy_txn_count, + ) .await; - assert_eq!(deposits as usize, pending.len()); + assert_eq!(deposits as usize, pending.len(), "{pending:?}"); assert_eq!(&wallet_address, &pending[0].account().into()); assert_eq!( U256::from(1500000000000000000u64), @@ -345,32 +437,72 @@ mod test { // check a few more cases let pending = l1_client - .get_finalized_deposits(Some(0), deposits + deploy_txn_count) + .get_finalized_deposits( + fee_contract_proxy.address(), + Some(0), + deposits + deploy_txn_count, + ) .await; assert_eq!(deposits as usize, pending.len()); - let pending = l1_client.get_finalized_deposits(Some(0), 0).await; + let pending = l1_client + .get_finalized_deposits(fee_contract_proxy.address(), Some(0), 0) + .await; assert_eq!(0, pending.len()); - let pending = l1_client.get_finalized_deposits(Some(0), 1).await; + let pending = l1_client + .get_finalized_deposits(fee_contract_proxy.address(), Some(0), 1) + .await; assert_eq!(0, pending.len()); let pending = l1_client - .get_finalized_deposits(Some(deploy_txn_count), deploy_txn_count) + .get_finalized_deposits( + fee_contract_proxy.address(), + Some(deploy_txn_count), + deploy_txn_count, + ) .await; assert_eq!(0, pending.len()); let pending = l1_client - .get_finalized_deposits(Some(deploy_txn_count), deploy_txn_count + 1) + .get_finalized_deposits( + fee_contract_proxy.address(), + Some(deploy_txn_count), + deploy_txn_count + 1, + ) .await; assert_eq!(1, pending.len()); // what happens if `new_finalized` is `0`? let pending = l1_client - .get_finalized_deposits(Some(deploy_txn_count), 0) + .get_finalized_deposits(fee_contract_proxy.address(), Some(deploy_txn_count), 0) .await; assert_eq!(0, pending.len()); Ok(()) } + + #[async_std::test] + async fn test_wait_for_finalized_block() { + setup_logging(); + setup_backtrace(); + + let anvil = Anvil::new().block_time(1u32).spawn(); + let l1_client = L1Client::new(anvil.endpoint().parse().unwrap(), 1); + let provider = &l1_client.provider; + + // Wait for a block 10 blocks in the future. + let block_height = provider.get_block_number().await.unwrap().as_u64(); + let block = l1_client.wait_for_finalized_block(block_height + 10).await; + assert_eq!(block.number, block_height + 10); + + // Compare against underlying provider. + let true_block = provider + .get_block(block_height + 10) + .await + .unwrap() + .unwrap(); + assert_eq!(block.timestamp, true_block.timestamp); + assert_eq!(block.hash, true_block.hash.unwrap()); + } } diff --git a/sequencer/src/lib.rs b/sequencer/src/lib.rs index 4d5221077..e1acc1ea5 100644 --- a/sequencer/src/lib.rs +++ b/sequencer/src/lib.rs @@ -4,23 +4,32 @@ pub mod catchup; mod chain_config; pub mod context; pub mod eth_signature_key; +pub mod genesis; mod header; pub mod hotshot_commitment; pub mod options; pub mod state_signature; +mod message_compat_tests; +mod reference_tests; + use anyhow::Context; use async_std::sync::RwLock; use async_trait::async_trait; -use block::entry::TxTableEntryWord; use catchup::{StateCatchup, StatePeers}; use context::SequencerContext; -use ethers::types::{Address, U256}; +use ethers::types::U256; +#[cfg(feature = "libp2p")] +use futures::FutureExt; +use genesis::{GenesisHeader, L1Finalized, Upgrade}; // Should move `STAKE_TABLE_CAPACITY` in the sequencer repo when we have variate stake table support +use catchup::BackoffParams; use l1_client::L1Client; +use libp2p::Multiaddr; +use network::libp2p::split_off_peer_id; use state::FeeAccount; use state_signature::static_stake_table_commitment; use url::Url; @@ -34,7 +43,7 @@ use hotshot::{ traits::{ election::static_committee::GeneralStaticCommittee, implementations::{ - derive_libp2p_peer_id, KeyPair, MemoryNetwork, NetworkingMetricsValue, PushCdnNetwork, + derive_libp2p_peer_id, CdnMetricsValue, KeyPair, MemoryNetwork, PushCdnNetwork, Topic, WrappedSignatureKey, }, }, @@ -47,7 +56,8 @@ use hotshot_orchestrator::{ }; use hotshot_types::{ consensus::CommitmentMap, - data::{DAProposal, VidDisperseShare, ViewNumber}, + constants::Base, + data::{DaProposal, QuorumProposal, VidDisperseShare, ViewNumber}, event::HotShotAction, light_client::{StateKeyPair, StateSignKey}, message::Proposal, @@ -57,17 +67,18 @@ use hotshot_types::{ metrics::Metrics, network::ConnectedNetwork, node_implementation::{NodeImplementation, NodeType}, + signature_key::{BuilderSignatureKey, StakeTableEntryType}, states::InstanceState, storage::Storage, }, utils::{BuilderCommitment, View}, ValidatorConfig, }; -use persistence::SequencerPersistence; +use persistence::{PersistenceOptions, SequencerPersistence}; use serde::{Deserialize, Serialize}; use snafu::Snafu; use std::{collections::BTreeMap, fmt::Debug, marker::PhantomData, net::SocketAddr, sync::Arc}; -use vbs::version::StaticVersionType; +use vbs::version::{StaticVersionType, Version}; #[cfg(feature = "libp2p")] use std::time::Duration; @@ -75,8 +86,9 @@ use std::time::Duration; #[cfg(feature = "libp2p")] use hotshot::traits::implementations::{CombinedNetworks, Libp2pNetwork}; -pub use block::payload::Payload; +pub use block::Payload; pub use chain_config::ChainConfig; +pub use genesis::Genesis; pub use header::Header; pub use l1_client::L1BlockInfo; pub use options::Options; @@ -117,7 +129,7 @@ pub type PrivKey = ::PrivateKey; impl NodeImplementation for Node { type QuorumNetwork = N::QuorumChannel; - type CommitteeNetwork = N::DAChannel; + type DaNetwork = N::DAChannel; type Storage = Arc>; } @@ -132,7 +144,7 @@ impl Storage for Arc> { async fn append_da( &self, - proposal: &Proposal>, + proposal: &Proposal>, ) -> anyhow::Result<()> { self.write().await.append_da(proposal).await } @@ -145,40 +157,65 @@ impl Storage for Arc> { async fn update_undecided_state( &self, - _leaves: CommitmentMap, - _state: BTreeMap>, + leaves: CommitmentMap, + state: BTreeMap>, ) -> anyhow::Result<()> { - Ok(()) + self.write() + .await + .update_undecided_state(leaves, state) + .await + } + + async fn append_proposal( + &self, + proposal: &Proposal>, + ) -> anyhow::Result<()> { + self.write().await.append_quorum_proposal(proposal).await } } #[derive(Debug, Clone)] pub struct NodeState { - chain_config: ChainConfig, - l1_client: L1Client, - peers: Arc, - genesis_state: ValidatedState, + pub node_id: u64, + pub chain_config: ChainConfig, + pub l1_client: L1Client, + pub peers: Arc, + pub genesis_header: GenesisHeader, + pub genesis_state: ValidatedState, + pub l1_genesis: Option, + pub upgrades: BTreeMap, + pub current_version: Version, } impl NodeState { pub fn new( + node_id: u64, chain_config: ChainConfig, l1_client: L1Client, catchup: impl StateCatchup + 'static, ) -> Self { Self { + node_id, chain_config, l1_client, peers: Arc::new(catchup), - genesis_state: Default::default(), + genesis_header: Default::default(), + genesis_state: ValidatedState { + chain_config: chain_config.into(), + ..Default::default() + }, + l1_genesis: None, + upgrades: Default::default(), + current_version: Base::VERSION, } } #[cfg(any(test, feature = "testing"))] pub fn mock() -> Self { Self::new( + 0, ChainConfig::default(), - L1Client::new("http://localhost:3331".parse().unwrap(), Address::default()), + L1Client::new("http://localhost:3331".parse().unwrap(), 10000), catchup::mock::MockStateCatchup::default(), ) } @@ -193,8 +230,28 @@ impl NodeState { self } - fn l1_client(&self) -> &L1Client { - &self.l1_client + pub fn with_chain_config(mut self, cfg: ChainConfig) -> Self { + self.chain_config = cfg; + self + } + + pub fn with_upgrades(mut self, upgrades: BTreeMap) -> Self { + self.upgrades = upgrades; + self + } +} + +// This allows us to turn on `Default` on InstanceState trait +// which is used in `HotShot` by `TestBuilderImplementation`. +#[cfg(any(test, feature = "testing"))] +impl Default for NodeState { + fn default() -> Self { + Self::new( + 1u64, + ChainConfig::default(), + L1Client::new("http://localhost:3331".parse().unwrap(), 10000), + catchup::mock::MockStateCatchup::default(), + ) } } @@ -203,7 +260,7 @@ impl InstanceState for NodeState {} impl NodeType for SeqTypes { type Time = ViewNumber; type BlockHeader = Header; - type BlockPayload = Payload; + type BlockPayload = Payload; type SignatureKey = PubKey; type Transaction = Transaction; type InstanceState = NodeState; @@ -246,33 +303,49 @@ pub struct NetworkParams { pub private_staking_key: BLSPrivKey, pub private_state_key: StateSignKey, pub state_peers: Vec, + pub catchup_backoff: BackoffParams, + /// The address to send to other Libp2p nodes to contact us pub libp2p_advertise_address: SocketAddr, /// The address to bind to for Libp2p pub libp2p_bind_address: SocketAddr, -} - -#[derive(Clone, Debug)] -pub struct BuilderParams { - pub prefunded_accounts: Vec
, + /// The (optional) bootstrap node addresses for Libp2p. If supplied, these will + /// override the bootstrap nodes specified in the config file. + pub libp2p_bootstrap_nodes: Option>, } pub struct L1Params { pub url: Url, + pub events_max_block_range: u64, } -#[allow(clippy::too_many_arguments)] -pub async fn init_node( +pub async fn init_node( + genesis: Genesis, network_params: NetworkParams, metrics: &dyn Metrics, - mut persistence: P, - builder_params: BuilderParams, + persistence_opt: P, l1_params: L1Params, - stake_table_capacity: usize, bind_version: Ver, - chain_config: ChainConfig, is_da: bool, -) -> anyhow::Result> { +) -> anyhow::Result> { + // Expose git information via status API. + metrics + .text_family( + "version".into(), + vec!["rev".into(), "desc".into(), "timestamp".into()], + ) + .create(vec![ + env!("VERGEN_GIT_SHA").into(), + env!("VERGEN_GIT_DESCRIBE").into(), + env!("VERGEN_GIT_COMMIT_TIMESTAMP").into(), + ]); + + // Stick our public key in `metrics` so it is easily accessible via the status API. + let pub_key = BLSPubKey::from_private(&network_params.private_staking_key); + metrics + .text_family("node".into(), vec!["key".into()]) + .create(vec![pub_key.to_string()]); + // Orchestrator client let validator_args = ValidatorArgs { url: network_params.orchestrator_url, @@ -282,7 +355,7 @@ pub async fn init_node::SignatureKey>(&my_config.private_key) .with_context(|| "Failed to derive Libp2p peer ID")?; - let (config, wait_for_orchestrator) = match persistence.load_config().await? { + let mut persistence = persistence_opt.clone().create().await?; + let (mut config, wait_for_orchestrator) = match persistence.load_config().await? { Some(config) => { tracing::info!("loaded network config from storage, rejoining existing network"); (config, false) @@ -306,13 +380,11 @@ pub async fn init_node, _>>() + .with_context(|| "Failed to parse peer ID from bootstrap node")?; + } else { + // If not, don't try launching with them. Eventually we may want to + // provide a default configuration here instead. + tracing::warn!("No libp2p configuration found, ignoring supplied bootstrap nodes"); + } + } + let node_index = config.node_index; // If we are a DA node, we need to subscribe to the DA topic let topics = { - let mut topics = vec!["Global".into()]; + let mut topics = vec![Topic::Global]; if is_da { - topics.push("DA".into()); + topics.push(Topic::Da); } topics }; @@ -346,37 +447,42 @@ pub async fn init_node( - config.clone(), - network_params.libp2p_bind_address, - &my_config.public_key, - // We need the private key so we can derive our Libp2p keypair - // (using https://docs.rs/blake3/latest/blake3/fn.derive_key.html) - &my_config.private_key, - ) - .await - .with_context(|| "Failed to create libp2p network")?; - - // Combine the communication channels - #[cfg(feature = "libp2p")] let (da_network, quorum_network) = { - ( - Arc::from(CombinedNetworks::new( - cdn_network.clone(), - p2p_network.clone(), - Duration::from_secs(1), - )), - Arc::from(CombinedNetworks::new( - cdn_network, - p2p_network, - Duration::from_secs(1), - )), + let p2p_network = Libp2pNetwork::from_config::( + config.clone(), + network_params.libp2p_bind_address, + &my_config.public_key, + // We need the private key so we can derive our Libp2p keypair + // (using https://docs.rs/blake3/latest/blake3/fn.derive_key.html) + &my_config.private_key, + hotshot::traits::implementations::Libp2pMetricsValue::new(metrics), ) + .await + .with_context(|| "Failed to create libp2p network")?; + + tracing::warn!("Waiting for at least one connection to be initialized"); + futures::select! { + _ = cdn_network.wait_for_ready().fuse() => { + tracing::warn!("CDN connection initialized"); + }, + _ = p2p_network.wait_for_ready().fuse() => { + tracing::warn!("P2P connection initialized"); + }, + }; + + // Combine the CDN and P2P networks + let network = Arc::from(CombinedNetworks::new( + cdn_network, + p2p_network, + Duration::from_secs(1), + )); + (Arc::clone(&network), network) }; // Wait for the CDN network to be ready if we're not using the P2P network @@ -395,25 +501,40 @@ pub async fn init_node Some(b), + Some(L1Finalized::Number { number }) => { + Some(l1_client.wait_for_finalized_block(number).await) + } + None => None, + }; let instance_state = NodeState { - chain_config, + chain_config: genesis.chain_config, l1_client, + genesis_header: genesis.header, genesis_state, - peers: Arc::new(StatePeers::::from_urls(network_params.state_peers)), + l1_genesis, + peers: catchup::local_and_remote( + persistence_opt, + StatePeers::::from_urls( + network_params.state_peers, + network_params.catchup_backoff, + ), + ) + .await, + node_id: node_index, + upgrades: genesis.upgrades, + current_version: Ver::VERSION, }; let mut ctx = SequencerContext::init( @@ -423,8 +544,7 @@ pub async fn init_node BuilderCommitment { pub mod testing { use super::*; use crate::{ - catchup::mock::MockStateCatchup, eth_signature_key::EthKeyPair, - persistence::no_storage::NoStorage, + catchup::mock::MockStateCatchup, + eth_signature_key::EthKeyPair, + persistence::no_storage::{self, NoStorage}, }; + use api::test_helpers::TestNetworkUpgrades; use committable::Committable; - use ethers::utils::{Anvil, AnvilInstance}; use futures::{ future::join_all, stream::{Stream, StreamExt}, }; + use genesis::Upgrade; use hotshot::traits::{ implementations::{MasterMap, MemoryNetwork}, BlockPayload, }; - use hotshot::types::{EventType::Decide, Message}; + use hotshot::types::EventType::Decide; + use hotshot_stake_table::vec_based::StakeTable; use hotshot_testing::block_builder::{ - BuilderTask, SimpleBuilderImplementation, TestBuilderImplementation, + BuilderTask, SimpleBuilderConfig, SimpleBuilderImplementation, TestBuilderImplementation, }; use hotshot_types::{ event::LeafInfo, - light_client::StateKeyPair, - traits::{ - block_contents::BlockHeader, metrics::NoMetrics, signature_key::BuilderSignatureKey, - }, - ExecutionType, HotShotConfig, PeerConfig, ValidatorConfig, + light_client::{CircuitField, StateKeyPair, StateVerKey}, + traits::{block_contents::BlockHeader, metrics::NoMetrics, stake_table::StakeTableScheme}, + ExecutionType, HotShotConfig, PeerConfig, }; use portpicker::pick_unused_port; use std::time::Duration; + use vbs::version::Version; - const STAKE_TABLE_CAPACITY_FOR_TEST: usize = 10; + const STAKE_TABLE_CAPACITY_FOR_TEST: u64 = 10; - pub async fn run_test_builder() -> (Option>>, Url) { + pub async fn run_test_builder(port: Option) -> (Box>, Url) { + let builder_config = if let Some(port) = port { + SimpleBuilderConfig { port } + } else { + SimpleBuilderConfig::default() + }; >::start( TestConfig::NUM_NODES, - (), + builder_config, + Default::default(), ) .await } @@ -483,10 +611,13 @@ pub mod testing { #[derive(Clone)] pub struct TestConfig { config: HotShotConfig, - priv_keys: Vec, - state_key_pairs: Vec, - master_map: Arc, PubKey>>, - anvil: Arc, + pub priv_keys: Vec, + pub state_key_pairs: Vec, + pub master_map: Arc>, + pub url: Url, + pub state_relay_url: Option, + pub builder_port: Option, + pub upgrades: Option, } impl Default for TestConfig { @@ -505,7 +636,7 @@ pub mod testing { .iter() .zip(&state_key_pairs) .map(|(pub_key, state_key_pair)| PeerConfig:: { - stake_table_entry: pub_key.get_stake_table_entry(1), + stake_table_entry: pub_key.stake_table_entry(1), state_ver_key: state_key_pair.ver_key(), }) .collect::>(); @@ -530,17 +661,20 @@ pub mod testing { my_own_validator_config: Default::default(), view_sync_timeout: Duration::from_secs(1), data_request_delay: Duration::from_secs(1), - //?? - builder_url: Url::parse(&format!( + builder_urls: vec1::vec1![Url::parse(&format!( "http://127.0.0.1:{}", pick_unused_port().unwrap() )) - .unwrap(), + .unwrap()], builder_timeout: Duration::from_secs(1), start_threshold: ( known_nodes_with_stake.clone().len() as u64, known_nodes_with_stake.clone().len() as u64, ), + start_proposing_view: 0, + stop_proposing_view: 0, + start_voting_view: 0, + stop_voting_view: 0, }; Self { @@ -548,13 +682,16 @@ pub mod testing { priv_keys, state_key_pairs, master_map, - anvil: Arc::new(Anvil::new().spawn()), + url: "http://localhost:8545".parse().unwrap(), + state_relay_url: None, + builder_port: None, + upgrades: None, } } } impl TestConfig { - pub const NUM_NODES: usize = 4; + pub const NUM_NODES: usize = 5; pub fn num_nodes(&self) -> usize { self.priv_keys.len() @@ -564,8 +701,32 @@ pub mod testing { &self.config } - pub fn set_builder_url(&mut self, builder_url: Url) { - self.config.builder_url = builder_url; + pub fn set_builder_urls(&mut self, builder_urls: vec1::Vec1) { + self.config.builder_urls = builder_urls; + } + + pub fn set_state_relay_url(&mut self, url: Url) { + self.state_relay_url = Some(url); + } + + pub fn default_with_l1(l1: Url) -> Self { + TestConfig { + url: l1, + ..Default::default() + } + } + + pub fn set_upgrade_parameters( + &mut self, + start_proposing_view: u64, + stop_proposing_view: u64, + start_voting_view: u64, + stop_voting_view: u64, + ) { + self.config.start_proposing_view = start_proposing_view; + self.config.stop_proposing_view = stop_proposing_view; + self.config.start_voting_view = start_voting_view; + self.config.stop_voting_view = stop_voting_view; } pub async fn init_nodes( @@ -576,46 +737,63 @@ pub mod testing { self.init_node( i, ValidatedState::default(), - NoStorage, + no_storage::Options, MockStateCatchup::default(), &NoMetrics, STAKE_TABLE_CAPACITY_FOR_TEST, bind_version, - true, + Default::default(), ) .await })) .await } + pub fn stake_table(&self) -> StakeTable { + let mut st = StakeTable::::new( + STAKE_TABLE_CAPACITY_FOR_TEST as usize, + ); + self.config + .known_nodes_with_stake + .iter() + .for_each(|config| { + st.register( + *config.stake_table_entry.key(), + config.stake_table_entry.stake(), + config.state_ver_key.clone(), + ) + .unwrap() + }); + st.advance(); + st.advance(); + st + } + #[allow(clippy::too_many_arguments)] - pub async fn init_node( + pub async fn init_node( &self, i: usize, mut state: ValidatedState, - persistence: P, + persistence_opt: P, catchup: impl StateCatchup + 'static, metrics: &dyn Metrics, - stake_table_capacity: usize, + stake_table_capacity: u64, bind_version: Ver, - is_da: bool, - ) -> SequencerContext { + upgrades: BTreeMap, + ) -> SequencerContext { let mut config = self.config.clone(); + let my_peer_config = &config.known_nodes_with_stake[i]; config.my_own_validator_config = ValidatorConfig { - public_key: config.known_nodes_with_stake[i].stake_table_entry.stake_key, + public_key: my_peer_config.stake_table_entry.stake_key, private_key: self.priv_keys[i].clone(), - stake_value: config.known_nodes_with_stake[i] - .stake_table_entry - .stake_amount - .as_u64(), + stake_value: my_peer_config.stake_table_entry.stake_amount.as_u64(), state_key_pair: self.state_key_pairs[i].clone(), - is_da, + is_da: config.known_da_nodes.contains(my_peer_config), }; let network = Arc::new(MemoryNetwork::new( config.my_own_validator_config.public_key, - NetworkingMetricsValue::new(metrics), - self.master_map.clone(), + &self.master_map, None, )); let networks = Networks { @@ -629,11 +807,13 @@ pub mod testing { tracing::info!(%builder_account, "prefunding builder account"); state.prefund_account(builder_account, U256::max_value().into()); let node_state = NodeState::new( + i as u64, ChainConfig::default(), - L1Client::new(self.anvil.endpoint().parse().unwrap(), Address::default()), - catchup, + L1Client::new(self.url.clone(), 1000), + catchup::local_and_remote(persistence_opt.clone(), catchup).await, ) - .with_genesis(state); + .with_genesis(state) + .with_upgrades(upgrades); tracing::info!( i, @@ -644,11 +824,10 @@ pub mod testing { SequencerContext::init( config, node_state, - persistence, + persistence_opt.create().await.unwrap(), networks, - None, + self.state_relay_url.clone(), metrics, - i as u64, stake_table_capacity, bind_version, ) @@ -677,12 +856,12 @@ pub mod testing { if let Decide { leaf_chain, .. } = event.event { if let Some(height) = leaf_chain.iter().find_map(|LeafInfo { leaf, .. }| { if leaf - .get_block_payload() + .block_payload() .as_ref()? - .transaction_commitments(leaf.get_block_header().metadata()) + .transaction_commitments(leaf.block_header().metadata()) .contains(&commitment) { - Some(leaf.get_block_header().block_number()) + Some(leaf.block_header().block_number()) } else { None } @@ -710,9 +889,10 @@ mod test { use hotshot_types::{ event::LeafInfo, traits::block_contents::{ - vid_commitment, BlockHeader, BlockPayload, GENESIS_VID_NUM_STORAGE_NODES, + vid_commitment, BlockHeader, BlockPayload, EncodeBytes, GENESIS_VID_NUM_STORAGE_NODES, }, }; + use sequencer_utils::AnvilOptions; use testing::{wait_for_decide_on_handle, TestConfig}; #[async_std::test] @@ -721,29 +901,29 @@ mod test { setup_backtrace(); let ver = SequencerVersion::instance(); // Assign `config` so it isn't dropped early. - let mut config = TestConfig::default(); + let anvil = AnvilOptions::default().spawn().await; + let url = anvil.url(); + let mut config = TestConfig::default_with_l1(url); - let (builder_task, builder_url) = run_test_builder().await; + let (builder_task, builder_url) = run_test_builder(None).await; - config.set_builder_url(builder_url); + config.set_builder_urls(vec1::vec1![builder_url]); let handles = config.init_nodes(ver).await; let handle_0 = &handles[0]; // Hook the builder up to the event stream from the first node - if let Some(builder_task) = builder_task { - builder_task.start(Box::new(handle_0.get_event_stream())); - } + builder_task.start(Box::new(handle_0.event_stream().await)); - let mut events = handle_0.get_event_stream(); + let mut events = handle_0.event_stream().await; for handle in handles.iter() { handle.start_consensus().await; } // Submit target transaction to handle - let txn = Transaction::new(Default::default(), vec![1, 2, 3]); + let txn = Transaction::new(NamespaceId::from(1), vec![1, 2, 3]); handles[0] .submit_transaction(txn.clone()) .await @@ -761,21 +941,21 @@ mod test { let success_height = 30; let ver = SequencerVersion::instance(); // Assign `config` so it isn't dropped early. - let mut config = TestConfig::default(); + let anvil = AnvilOptions::default().spawn().await; + let url = anvil.url(); + let mut config = TestConfig::default_with_l1(url); - let (builder_task, builder_url) = run_test_builder().await; + let (builder_task, builder_url) = run_test_builder(None).await; - config.set_builder_url(builder_url); + config.set_builder_urls(vec1::vec1![builder_url]); let handles = config.init_nodes(ver).await; let handle_0 = &handles[0]; - let mut events = handle_0.get_event_stream(); + let mut events = handle_0.event_stream().await; // Hook the builder up to the event stream from the first node - if let Some(builder_task) = builder_task { - builder_task.start(Box::new(handle_0.get_event_stream())); - } + builder_task.start(Box::new(handle_0.event_stream().await)); for handle in handles.iter() { handle.start_consensus().await; @@ -784,12 +964,12 @@ mod test { let mut parent = { // TODO refactor repeated code from other tests let (genesis_payload, genesis_ns_table) = - Payload::from_transactions([], Arc::new(NodeState::mock())).unwrap(); + Payload::from_transactions([], &ValidatedState::default(), &NodeState::mock()) + .await + .unwrap(); let genesis_commitment = { // TODO we should not need to collect payload bytes just to compute vid_commitment - let payload_bytes = genesis_payload - .encode() - .expect("unable to encode genesis payload"); + let payload_bytes = genesis_payload.encode(); vid_commitment(&payload_bytes, GENESIS_VID_NUM_STORAGE_NODES) }; let genesis_state = NodeState::mock(); @@ -812,7 +992,7 @@ mod test { // Check that each successive header satisfies invariants relative to its parent: all // the fields which should be monotonic are. for LeafInfo { leaf, .. } in leaf_chain.iter().rev() { - let header = leaf.get_block_header().clone(); + let header = leaf.block_header().clone(); if header.height == 0 { parent = header; continue; diff --git a/sequencer/src/main.rs b/sequencer/src/main.rs index 53cb80d41..7eb2b8d05 100644 --- a/sequencer/src/main.rs +++ b/sequencer/src/main.rs @@ -9,7 +9,7 @@ use sequencer::{ api::{self, data_source::DataSourceOptions}, init_node, options::{Modules, Options}, - persistence, BuilderParams, ChainConfig, L1Params, NetworkParams, + persistence, Genesis, L1Params, NetworkParams, }; use vbs::version::StaticVersionType; @@ -18,10 +18,10 @@ async fn main() -> anyhow::Result<()> { setup_logging(); setup_backtrace(); - tracing::info!("sequencer starting up"); + tracing::warn!("sequencer starting up"); let opt = Options::parse(); - tracing::warn!("options: {:?}", opt); let mut modules = opt.modules(); + tracing::warn!("modules: {:?}", modules); if let Some(storage) = modules.storage_fs.take() { init_with_storage(modules, opt, storage, SEQUENCER_VERSION).await @@ -48,14 +48,13 @@ async fn init_with_storage( where S: DataSourceOptions, { + let genesis = Genesis::from_file(&opt.genesis_file)?; + tracing::info!(?genesis, "genesis"); + let (private_staking_key, private_state_key) = opt.private_keys()?; - let stake_table_capacity = opt.stake_table_capacity; - let chain_config = ChainConfig::new(opt.chain_id, opt.max_block_size, opt.base_fee); let l1_params = L1Params { url: opt.l1_provider_url, - }; - let builder_params = BuilderParams { - prefunded_accounts: opt.prefunded_builder_accounts, + events_max_block_range: opt.l1_events_max_block_range, }; // Parse supplied Libp2p addresses to their socket form @@ -78,11 +77,13 @@ where cdn_endpoint: opt.cdn_endpoint, libp2p_advertise_address, libp2p_bind_address, + libp2p_bootstrap_nodes: opt.libp2p_bootstrap_nodes, orchestrator_url: opt.orchestrator_url, state_relay_server_url: opt.state_relay_server_url, private_staking_key, private_state_key, state_peers: opt.state_peers, + catchup_backoff: opt.catchup_backoff, }; // Initialize HotShot. If the user requested the HTTP module, we must initialize the handle in @@ -107,24 +108,27 @@ where if let Some(catchup) = modules.catchup { http_opt = http_opt.catchup(catchup); } + if let Some(hotshot_events) = modules.hotshot_events { http_opt = http_opt.hotshot_events(hotshot_events); } - - let storage = storage_opt.create().await?; + if let Some(explorer) = modules.explorer { + http_opt = http_opt.explorer(explorer); + } + if let Some(config) = modules.config { + http_opt = http_opt.config(config); + } http_opt .serve( move |metrics| { async move { init_node( + genesis, network_params, &*metrics, - storage, - builder_params, + storage_opt, l1_params, - stake_table_capacity, bind_version, - chain_config, opt.is_da, ) .await @@ -138,14 +142,12 @@ where } None => { init_node( + genesis, network_params, &NoMetrics, - storage_opt.create().await?, - builder_params, + storage_opt, l1_params, - stake_table_capacity, bind_version, - chain_config, opt.is_da, ) .await? @@ -158,3 +160,111 @@ where Ok(()) } + +#[cfg(test)] +mod test { + use super::*; + use async_std::task::spawn; + use es_version::SequencerVersion; + use hotshot_types::{light_client::StateKeyPair, traits::signature_key::SignatureKey}; + use portpicker::pick_unused_port; + use sequencer::{ + api::options::{Http, Status}, + genesis::StakeTableConfig, + persistence::fs, + PubKey, + }; + use std::time::Duration; + use surf_disco::{error::ClientError, Client, Url}; + use tempfile::TempDir; + + #[async_std::test] + async fn test_startup_before_orchestrator() { + setup_logging(); + setup_backtrace(); + + let (pub_key, priv_key) = PubKey::generated_from_seed_indexed([0; 32], 0); + let state_key = StateKeyPair::generate_from_seed_indexed([0; 32], 0); + + let port = pick_unused_port().unwrap(); + let tmp = TempDir::new().unwrap(); + + let genesis_file = tmp.path().join("genesis.toml"); + let genesis = Genesis { + chain_config: Default::default(), + stake_table: StakeTableConfig { capacity: 10 }, + accounts: Default::default(), + l1_finalized: Default::default(), + header: Default::default(), + upgrades: Default::default(), + }; + genesis.to_file(&genesis_file).unwrap(); + + let modules = Modules { + http: Some(Http::with_port(port)), + status: Some(Status), + ..Default::default() + }; + let opt = Options::parse_from([ + "sequencer", + "--private-staking-key", + &priv_key.to_string(), + "--private-state-key", + &state_key.sign_key_ref().to_string(), + "--genesis-file", + &genesis_file.display().to_string(), + ]); + + // Start the sequencer in a background task. This process will not complete, because it will + // be waiting for the orchestrator, but it should at least start up the API server and + // populate some metrics. + tracing::info!(port, "starting sequencer"); + let task = spawn(async move { + if let Err(err) = init_with_storage( + modules, + opt, + fs::Options::new(tmp.path().into()), + SEQUENCER_VERSION, + ) + .await + { + tracing::error!("failed to start sequencer: {err:#}"); + } + }); + + // The healthcheck should eventually come up even though the node is waiting for the + // orchestrator. + tracing::info!("waiting for API to start"); + let url: Url = format!("http://localhost:{port}").parse().unwrap(); + let client = Client::::new(url.clone()); + assert!(client.connect(Some(Duration::from_secs(60))).await); + client.get::<()>("healthcheck").send().await.unwrap(); + + // The metrics should include information about the node and software version. surf-disco + // doesn't currently support fetching a plaintext file, so we use a raw reqwest client. + let res = reqwest::get(url.join("/status/metrics").unwrap()) + .await + .unwrap(); + assert!(res.status().is_success(), "{}", res.status()); + let metrics = res.text().await.unwrap(); + let lines = metrics.lines().collect::>(); + assert!( + lines.contains(&format!("consensus_node{{key=\"{pub_key}\"}} 1").as_str()), + "{lines:#?}" + ); + assert!( + lines.contains( + &format!( + "consensus_version{{desc=\"{}\",rev=\"{}\",timestamp=\"{}\"}} 1", + env!("VERGEN_GIT_DESCRIBE"), + env!("VERGEN_GIT_SHA"), + env!("VERGEN_GIT_COMMIT_TIMESTAMP"), + ) + .as_str() + ), + "{lines:#?}" + ); + + task.cancel().await; + } +} diff --git a/sequencer/src/message_compat_tests.rs b/sequencer/src/message_compat_tests.rs new file mode 100644 index 000000000..f794fdd60 --- /dev/null +++ b/sequencer/src/message_compat_tests.rs @@ -0,0 +1,295 @@ +#![cfg(test)] +//! Serialization compatibility tests for consensus messages. +//! +//! This test generates a test vector containing one variant of each type of consensus message, +//! instantiated with sequencer types. A serialized version of this vector is written to a file and +//! checked into the repo under `data/messages.json`. If the serialization of the generated test +//! vector does not match the committed file, the test fails, indicating a potential API +//! incompatibility. +//! +//! If this test fails and you intended to change the consensus API, you may simply replace the +//! serialized file as indicated in the test output. Note however that this may break compatibility +//! with older releases, and your pull request should explain why this is OK. +//! +//! If this test is failing and you did not intend to change the consensus API, figure out what +//! code changed caused the serialization change and revert it. + +use crate::{Leaf, NodeState, Payload, PubKey, SeqTypes, Transaction, ValidatedState}; +use committable::Committable; +use es_version::SequencerVersion; +use hotshot::traits::election::static_committee::GeneralStaticCommittee; +use hotshot_types::{ + data::{ + DaProposal, QuorumProposal, UpgradeProposal, VidDisperse, VidDisperseShare, + ViewChangeEvidence, ViewNumber, + }, + message::{ + DaConsensusMessage, DataMessage, GeneralConsensusMessage, Message, MessageKind, Proposal, + SequencingMessage, + }, + simple_certificate::{ + DaCertificate, QuorumCertificate, TimeoutCertificate, UpgradeCertificate, + ViewSyncCommitCertificate2, ViewSyncFinalizeCertificate2, ViewSyncPreCommitCertificate2, + }, + simple_vote::{ + DaData, DaVote, QuorumData, QuorumVote, TimeoutData, TimeoutVote, UpgradeProposalData, + UpgradeVote, ViewSyncCommitData, ViewSyncCommitVote, ViewSyncFinalizeData, + ViewSyncFinalizeVote, ViewSyncPreCommitData, ViewSyncPreCommitVote, + }, + traits::{ + node_implementation::ConsensusTime, signature_key::SignatureKey, BlockPayload, EncodeBytes, + }, + vid::vid_scheme, +}; +use jf_vid::VidScheme; +use pretty_assertions::assert_eq; +use serde_json::Value; +use std::path::Path; +use vbs::{version::Version, BinarySerializer}; + +type Serializer = vbs::Serializer; + +#[async_std::test] +async fn test_message_compat() { + let (sender, priv_key) = PubKey::generated_from_seed_indexed(Default::default(), 0); + let signature = PubKey::sign(&priv_key, &[]).unwrap(); + let membership = GeneralStaticCommittee::new(&[], vec![sender.stake_table_entry(1)], vec![], 0); + let upgrade_data = UpgradeProposalData { + old_version: Version { major: 0, minor: 1 }, + new_version: Version { major: 1, minor: 0 }, + decide_by: ViewNumber::genesis(), + new_version_hash: Default::default(), + old_version_last_view: ViewNumber::genesis(), + new_version_first_view: ViewNumber::genesis(), + }; + let leaf = Leaf::genesis(&ValidatedState::default(), &NodeState::mock()).await; + let block_header = leaf.block_header().clone(); + let transaction = Transaction::new(1.into(), vec![1, 2, 3]); + let (payload, metadata) = Payload::from_transactions( + [transaction.clone()], + &ValidatedState::default(), + &NodeState::mock(), + ) + .await + .unwrap(); + let view_sync_pre_commit_data = ViewSyncPreCommitData { + relay: 0, + round: ViewNumber::genesis(), + }; + let view_sync_commit_data = ViewSyncCommitData { + relay: 0, + round: ViewNumber::genesis(), + }; + let view_sync_finalize_data = ViewSyncFinalizeData { + relay: 0, + round: ViewNumber::genesis(), + }; + let timeout_data = TimeoutData { + view: ViewNumber::genesis(), + }; + let da_data = DaData { + payload_commit: block_header.payload_commitment, + }; + + let consensus_messages = [ + GeneralConsensusMessage::Proposal(Proposal { + data: QuorumProposal { + block_header, + view_number: ViewNumber::genesis(), + justify_qc: QuorumCertificate::genesis( + &ValidatedState::default(), + &NodeState::mock(), + ) + .await, + upgrade_certificate: Some(UpgradeCertificate { + data: upgrade_data.clone(), + vote_commitment: upgrade_data.commit(), + view_number: ViewNumber::genesis(), + signatures: Default::default(), + _pd: Default::default(), + }), + proposal_certificate: Some(ViewChangeEvidence::Timeout(TimeoutCertificate { + data: timeout_data.clone(), + vote_commitment: timeout_data.commit(), + view_number: ViewNumber::genesis(), + signatures: Default::default(), + _pd: Default::default(), + })), + }, + signature: signature.clone(), + _pd: Default::default(), + }), + GeneralConsensusMessage::Vote(QuorumVote { + signature: (sender, signature.clone()), + data: QuorumData { + leaf_commit: leaf.commit(), + }, + view_number: ViewNumber::genesis(), + }), + GeneralConsensusMessage::ViewSyncPreCommitVote(ViewSyncPreCommitVote { + signature: (sender, signature.clone()), + data: view_sync_pre_commit_data.clone(), + view_number: ViewNumber::genesis(), + }), + GeneralConsensusMessage::ViewSyncCommitVote(ViewSyncCommitVote { + signature: (sender, signature.clone()), + data: view_sync_commit_data.clone(), + view_number: ViewNumber::genesis(), + }), + GeneralConsensusMessage::ViewSyncFinalizeVote(ViewSyncFinalizeVote { + signature: (sender, signature.clone()), + data: view_sync_finalize_data.clone(), + view_number: ViewNumber::genesis(), + }), + GeneralConsensusMessage::ViewSyncPreCommitCertificate(ViewSyncPreCommitCertificate2 { + data: view_sync_pre_commit_data.clone(), + vote_commitment: view_sync_pre_commit_data.commit(), + view_number: ViewNumber::genesis(), + signatures: Default::default(), + _pd: Default::default(), + }), + GeneralConsensusMessage::ViewSyncCommitCertificate(ViewSyncCommitCertificate2 { + data: view_sync_commit_data.clone(), + vote_commitment: view_sync_commit_data.commit(), + view_number: ViewNumber::genesis(), + signatures: Default::default(), + _pd: Default::default(), + }), + GeneralConsensusMessage::ViewSyncFinalizeCertificate(ViewSyncFinalizeCertificate2 { + data: view_sync_finalize_data.clone(), + vote_commitment: view_sync_finalize_data.commit(), + view_number: ViewNumber::genesis(), + signatures: Default::default(), + _pd: Default::default(), + }), + GeneralConsensusMessage::TimeoutVote(TimeoutVote { + signature: (sender, signature.clone()), + data: timeout_data, + view_number: ViewNumber::genesis(), + }), + GeneralConsensusMessage::UpgradeProposal(Proposal { + data: UpgradeProposal { + upgrade_proposal: upgrade_data.clone(), + view_number: ViewNumber::genesis(), + }, + signature: signature.clone(), + _pd: Default::default(), + }), + GeneralConsensusMessage::UpgradeVote(UpgradeVote { + signature: (sender, signature.clone()), + data: upgrade_data, + view_number: ViewNumber::genesis(), + }), + ]; + let da_messages = [ + DaConsensusMessage::DaProposal(Proposal { + data: DaProposal { + encoded_transactions: payload.encode(), + metadata, + view_number: ViewNumber::genesis(), + }, + signature: signature.clone(), + _pd: Default::default(), + }), + DaConsensusMessage::DaVote(DaVote { + signature: (sender, signature.clone()), + data: da_data.clone(), + view_number: ViewNumber::genesis(), + }), + DaConsensusMessage::DaCertificate(DaCertificate { + data: da_data.clone(), + vote_commitment: da_data.commit(), + view_number: ViewNumber::genesis(), + signatures: Default::default(), + _pd: Default::default(), + }), + DaConsensusMessage::VidDisperseMsg(Proposal { + data: VidDisperseShare::from_vid_disperse(VidDisperse::from_membership( + ViewNumber::genesis(), + vid_scheme(1).disperse(payload.encode()).unwrap(), + &membership, + )) + .remove(0), + signature: signature.clone(), + _pd: Default::default(), + }), + ]; + let data_messages = [DataMessage::SubmitTransaction( + transaction, + ViewNumber::genesis(), + )]; + + let seq_messages = consensus_messages + .into_iter() + .map(SequencingMessage::General) + .chain(da_messages.into_iter().map(SequencingMessage::Da)); + let messages = seq_messages + .map(MessageKind::Consensus) + .chain(data_messages.into_iter().map(MessageKind::Data)) + .map(|kind| Message { kind, sender }) + .collect::>>(); + + // Load the expected serialization from the repo. + let data_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../data"); + let expected_bytes = std::fs::read(data_dir.join("messages.json")).unwrap(); + let expected: Value = serde_json::from_slice(&expected_bytes).unwrap(); + + // Ensure the current serialization implementation generates the same JSON as the committed + // reference. + let actual = serde_json::to_value(&messages).unwrap(); + if actual != expected { + let actual_pretty = serde_json::to_string_pretty(&actual).unwrap(); + let expected_pretty = serde_json::to_string_pretty(&expected).unwrap(); + + // Write the actual output to a file to make it easier to compare with/replace the expected + // file if the serialization change was actually intended. + let actual_path = data_dir.join("messages-actual.json"); + std::fs::write(&actual_path, actual_pretty.as_bytes()).unwrap(); + + // Fail the test with an assertion that outputs a nice diff between the prettified JSON + // objects. + assert_eq!( + expected_pretty, + actual_pretty, + r#" + Serialized messages do not match expected JSON. The actual serialization has been written to {}. + If you intended to make a breaking change to the API you may replace the reference JSON file + /data/messages.json with /data/messages-actual.json. Otherwise, revert your changes which have + caused a change in the serialization of HotShot messages. + "#, + actual_path.display() + ); + } + + // Ensure the current `Message` type can be parsed from the committed reference JSON. + let parsed: Vec> = serde_json::from_value(expected).unwrap(); + assert_eq!(parsed, messages); + + // Ensure the current serialization implementation generates the same binary output as the + // committed reference. + let expected = std::fs::read(data_dir.join("messages.bin")).unwrap(); + let actual = Serializer::serialize(&messages).unwrap(); + if actual != expected { + // Write the actual output to a file to make it easier to compare with/replace the expected + // file if the serialization change was actually intended. + let actual_path = data_dir.join("messages-actual.bin"); + std::fs::write(&actual_path, &actual).unwrap(); + + // Fail the test with an assertion that outputs a diff. + assert_eq!( + expected, + actual, + r#" + Serialized messages do not match expected binary. The actual serialization has been written to + {}. If you intended to make a breaking change to the API you may replace the reference binary + file /data/messages.bin with /data/messages-actual.bin. Otherwise, revert your changes which + have caused a change in the serialization of HotShot messages. + "#, + actual_path.display() + ); + } + + // Ensure the current `Message` type can be parsed from the committed reference binary. + let parsed: Vec> = Serializer::deserialize(&expected).unwrap(); + assert_eq!(parsed, messages); +} diff --git a/sequencer/src/network.rs b/sequencer/src/network.rs deleted file mode 100644 index 8ef789181..000000000 --- a/sequencer/src/network.rs +++ /dev/null @@ -1,137 +0,0 @@ -use hotshot_types::message::Message; - -use super::*; - -pub trait Type: 'static { - type DAChannel: ConnectedNetwork, PubKey>; - type QuorumChannel: ConnectedNetwork, PubKey>; -} - -#[derive(Clone, Copy, Default)] -pub struct Production; - -#[cfg(feature = "libp2p")] -impl Type for Production { - type DAChannel = CombinedNetworks; - type QuorumChannel = CombinedNetworks; -} - -#[cfg(not(feature = "libp2p"))] -impl Type for Production { - type DAChannel = PushCdnNetwork; - type QuorumChannel = PushCdnNetwork; -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct Memory; - -impl Type for Memory { - type DAChannel = MemoryNetwork, PubKey>; - type QuorumChannel = MemoryNetwork, PubKey>; -} - -/// Trait implementations for the CDN -pub mod cdn { - use std::marker::PhantomData; - - use bincode::Options; - use cdn_broker::reexports::{ - connection::{ - protocols::{Quic, Tcp}, - NoMiddleware, TrustedMiddleware, UntrustedMiddleware, - }, - crypto::signature::{Serializable, SignatureScheme}, - def::{ConnectionDef, RunDef}, - discovery::{Embedded, Redis}, - }; - use hotshot::types::SignatureKey; - use hotshot_types::{traits::node_implementation::NodeType, utils::bincode_opts}; - - /// A wrapped `SignatureKey`. We need to implement the Push CDN's `SignatureScheme` - /// trait in order to sign and verify messages to/from the CDN. - #[derive(Clone, Eq, PartialEq)] - pub struct WrappedSignatureKey(pub T); - impl SignatureScheme for WrappedSignatureKey { - type PrivateKey = T::PrivateKey; - type PublicKey = Self; - - /// Sign a message of arbitrary data and return the serialized signature - fn sign(private_key: &Self::PrivateKey, message: &[u8]) -> anyhow::Result> { - let signature = T::sign(private_key, message)?; - // TODO: replace with rigorously defined serialization scheme... - // why did we not make `PureAssembledSignatureType` be `CanonicalSerialize + CanonicalDeserialize`? - Ok(bincode_opts().serialize(&signature)?) - } - - /// Verify a message of arbitrary data and return the result - fn verify(public_key: &Self::PublicKey, message: &[u8], signature: &[u8]) -> bool { - // TODO: replace with rigorously defined signing scheme - let signature: T::PureAssembledSignatureType = - match bincode_opts().deserialize(signature) { - Ok(key) => key, - Err(_) => return false, - }; - - public_key.0.validate(&signature, message) - } - } - - /// We need to implement the `Serializable` so the Push CDN can serialize the signatures - /// and public keys and send them over the wire. - impl Serializable for WrappedSignatureKey { - fn serialize(&self) -> anyhow::Result> { - Ok(self.0.to_bytes()) - } - - fn deserialize(serialized: &[u8]) -> anyhow::Result { - Ok(WrappedSignatureKey(T::from_bytes(serialized)?)) - } - } - - /// The production run definition for the Push CDN. - /// Uses the real protocols and a Redis discovery client. - pub struct ProductionDef(PhantomData); - impl RunDef for ProductionDef { - type User = UserDef; - type Broker = BrokerDef; - type DiscoveryClientType = Redis; - } - - /// The user definition for the Push CDN. - /// Uses the Quic protocol and untrusted middleware. - pub struct UserDef(PhantomData); - impl ConnectionDef for UserDef { - type Scheme = WrappedSignatureKey; - type Protocol = Quic; - type Middleware = UntrustedMiddleware; - } - - /// The broker definition for the Push CDN. - /// Uses the TCP protocol and trusted middleware. - pub struct BrokerDef(PhantomData); - impl ConnectionDef for BrokerDef { - type Scheme = WrappedSignatureKey; - type Protocol = Tcp; - type Middleware = TrustedMiddleware; - } - - /// The client definition for the Push CDN. Uses the Quic - /// protocol and no middleware. Differs from the user - /// definition in that is on the client-side. - #[derive(Clone)] - pub struct ClientDef(PhantomData); - impl ConnectionDef for ClientDef { - type Scheme = WrappedSignatureKey; - type Protocol = Quic; - type Middleware = NoMiddleware; - } - - /// The testing run definition for the Push CDN. - /// Uses the real protocols, but with an embedded discovery client. - pub struct TestingDef(PhantomData); - impl RunDef for TestingDef { - type User = UserDef; - type Broker = BrokerDef; - type DiscoveryClientType = Embedded; - } -} diff --git a/sequencer/src/network/cdn.rs b/sequencer/src/network/cdn.rs new file mode 100644 index 000000000..a3e6b7074 --- /dev/null +++ b/sequencer/src/network/cdn.rs @@ -0,0 +1,118 @@ +/// Trait implementations for the CDN +use std::marker::PhantomData; + +use bincode::Options; +use cdn_broker::reexports::{ + connection::protocols::{Quic, Tcp}, + crypto::signature::{Serializable, SignatureScheme}, + def::{ConnectionDef, RunDef, Topic as TopicTrait}, + discovery::{Embedded, Redis}, +}; +use hotshot::{traits::implementations::Topic as HotShotTopic, types::SignatureKey}; +use hotshot_types::{traits::node_implementation::NodeType, utils::bincode_opts}; +use num_enum::{IntoPrimitive, TryFromPrimitive}; +use static_assertions::const_assert_eq; + +/// The enum for the topics we can subscribe to in the Push CDN +#[repr(u8)] +#[derive(IntoPrimitive, TryFromPrimitive, Clone, PartialEq, Eq)] +pub enum Topic { + /// The global topic + Global = 0, + /// The DA topic + Da = 1, +} + +// Make sure the topics are the same as defined in `HotShot`. +const_assert_eq!(Topic::Global as u8, HotShotTopic::Global as u8); +const_assert_eq!(Topic::Da as u8, HotShotTopic::Da as u8); + +/// Implement the `TopicTrait` for our `Topic` enum. This lets us define +/// compatible topics at the broker-level. Others will be rejected. +impl TopicTrait for Topic {} + +/// A wrapped `SignatureKey`. We need to implement the Push CDN's `SignatureScheme` +/// trait in order to sign and verify messages to/from the CDN. +#[derive(Clone, Eq, PartialEq)] +pub struct WrappedSignatureKey(pub T); +impl SignatureScheme for WrappedSignatureKey { + type PrivateKey = T::PrivateKey; + type PublicKey = Self; + + /// Sign a message of arbitrary data and return the serialized signature + fn sign(private_key: &Self::PrivateKey, message: &[u8]) -> anyhow::Result> { + let signature = T::sign(private_key, message)?; + // TODO: replace with rigorously defined serialization scheme... + // why did we not make `PureAssembledSignatureType` be `CanonicalSerialize + CanonicalDeserialize`? + Ok(bincode_opts().serialize(&signature)?) + } + + /// Verify a message of arbitrary data and return the result + fn verify(public_key: &Self::PublicKey, message: &[u8], signature: &[u8]) -> bool { + // TODO: replace with rigorously defined signing scheme + let signature: T::PureAssembledSignatureType = match bincode_opts().deserialize(signature) { + Ok(key) => key, + Err(_) => return false, + }; + + public_key.0.validate(&signature, message) + } +} + +/// We need to implement the `Serializable` so the Push CDN can serialize the signatures +/// and public keys and send them over the wire. +impl Serializable for WrappedSignatureKey { + fn serialize(&self) -> anyhow::Result> { + Ok(self.0.to_bytes()) + } + + fn deserialize(serialized: &[u8]) -> anyhow::Result { + Ok(WrappedSignatureKey(T::from_bytes(serialized)?)) + } +} + +/// The production run definition for the Push CDN. +/// Uses the real protocols and a Redis discovery client. +pub struct ProductionDef(PhantomData); +impl RunDef for ProductionDef { + type User = UserDef; + type Broker = BrokerDef; + type DiscoveryClientType = Redis; + type Topic = Topic; +} + +/// The user definition for the Push CDN. +/// Uses the Quic protocol and untrusted middleware. +pub struct UserDef(PhantomData); +impl ConnectionDef for UserDef { + type Scheme = WrappedSignatureKey; + type Protocol = Quic; +} + +/// The broker definition for the Push CDN. +/// Uses the TCP protocol and trusted middleware. +pub struct BrokerDef(PhantomData); +impl ConnectionDef for BrokerDef { + type Scheme = WrappedSignatureKey; + type Protocol = Tcp; +} + +/// The client definition for the Push CDN. Uses the Quic +/// protocol and no middleware. Differs from the user +/// definition in that is on the client-side. +#[derive(Clone)] +pub struct ClientDef(PhantomData); +impl ConnectionDef for ClientDef { + type Scheme = WrappedSignatureKey; + type Protocol = Quic; +} + +/// The testing run definition for the Push CDN. +/// Uses the real protocols, but with an embedded discovery client. +pub struct TestingDef(PhantomData); +impl RunDef for TestingDef { + type User = UserDef; + type Broker = BrokerDef; + type DiscoveryClientType = Embedded; + type Topic = Topic; +} diff --git a/sequencer/src/network/libp2p.rs b/sequencer/src/network/libp2p.rs new file mode 100644 index 000000000..f60c2d4fc --- /dev/null +++ b/sequencer/src/network/libp2p.rs @@ -0,0 +1,14 @@ +use anyhow::Result; +use libp2p::{multiaddr::Protocol, Multiaddr, PeerId}; + +/// Split off the peer ID from a multiaddress, returning the shortened address and the peer ID. +/// +/// # Errors +/// - If the last protocol in the address is not a peer ID. +pub fn split_off_peer_id(mut address: Multiaddr) -> Result<(PeerId, Multiaddr)> { + let Some(Protocol::P2p(peer_id)) = address.pop() else { + return Err(anyhow::anyhow!("Failed to parse peer ID from address")); + }; + + Ok((peer_id, address)) +} diff --git a/sequencer/src/network/mod.rs b/sequencer/src/network/mod.rs new file mode 100644 index 000000000..0edf3e634 --- /dev/null +++ b/sequencer/src/network/mod.rs @@ -0,0 +1,32 @@ +use super::*; + +pub mod cdn; +pub mod libp2p; + +pub trait Type: 'static { + type DAChannel: ConnectedNetwork; + type QuorumChannel: ConnectedNetwork; +} + +#[derive(Clone, Copy, Default)] +pub struct Production; + +#[cfg(feature = "libp2p")] +impl Type for Production { + type DAChannel = CombinedNetworks; + type QuorumChannel = CombinedNetworks; +} + +#[cfg(not(feature = "libp2p"))] +impl Type for Production { + type DAChannel = PushCdnNetwork; + type QuorumChannel = PushCdnNetwork; +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct Memory; + +impl Type for Memory { + type DAChannel = MemoryNetwork; + type QuorumChannel = MemoryNetwork; +} diff --git a/sequencer/src/options.rs b/sequencer/src/options.rs index 779486a87..68a814811 100644 --- a/sequencer/src/options.rs +++ b/sequencer/src/options.rs @@ -1,4 +1,4 @@ -use crate::{api, persistence}; +use crate::{api, catchup::BackoffParams, persistence}; use anyhow::{bail, Context}; use bytesize::ByteSize; use clap::{error::ErrorKind, Args, FromArgMatches, Parser}; @@ -6,14 +6,16 @@ use cld::ClDuration; use core::fmt::Display; use derivative::Derivative; use derive_more::From; -use ethers::types::{Address, U256}; -use hotshot_stake_table::config::STAKE_TABLE_CAPACITY; use hotshot_types::light_client::StateSignKey; use hotshot_types::signature_key::BLSPrivKey; +use libp2p::Multiaddr; use snafu::Snafu; use std::{ + cmp::Ordering, collections::{HashMap, HashSet}, + fmt::{self, Formatter}, iter::once, + num::ParseIntError, path::PathBuf, str::FromStr, time::Duration, @@ -43,10 +45,6 @@ use url::Url; #[derive(Parser, Clone, Derivative)] #[derivative(Debug(bound = ""))] pub struct Options { - /// Unique identifier for this instance of the sequencer network. - #[clap(long, env = "ESPRESSO_SEQUENCER_CHAIN_ID", default_value = "0")] - pub chain_id: u16, - /// URL of the HotShot orchestrator. #[clap( short, @@ -69,7 +67,6 @@ pub struct Options { /// The address to bind to for Libp2p (in `host:port` form) #[clap( - short, long, env = "ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS", default_value = "0.0.0.0:1769" @@ -79,16 +76,26 @@ pub struct Options { /// The address we advertise to other nodes as being a Libp2p endpoint. /// Should be supplied in `host:port` form. #[clap( - short, long, env = "ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS", default_value = "localhost:1769" )] pub libp2p_advertise_address: String, + /// A comma-separated list of Libp2p multiaddresses to use as bootstrap + /// nodes. + /// + /// Overrides those loaded from the `HotShot` config. + #[clap( + long, + env = "ESPRESSO_SEQUENCER_LIBP2P_BOOTSTRAP_NODES", + value_delimiter = ',', + num_args = 1.. + )] + pub libp2p_bootstrap_nodes: Option>, + /// URL of the Light Client State Relay Server #[clap( - short, long, env = "ESPRESSO_STATE_RELAY_SERVER_URL", default_value = "http://localhost:8083" @@ -96,6 +103,15 @@ pub struct Options { #[derivative(Debug(format_with = "Display::fmt"))] pub state_relay_server_url: Url, + /// Path to TOML file containing genesis state. + #[clap( + long, + name = "GENESIS_FILE", + env = "ESPRESSO_SEQUENCER_GENESIS_FILE", + default_value = "/genesis/demo.toml" + )] + pub genesis_file: PathBuf, + /// Path to file containing private keys. /// /// The file should follow the .env format, with two keys: @@ -112,7 +128,7 @@ pub struct Options { #[clap( long, env = "ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY", - conflicts_with = "key_file" + conflicts_with = "KEY_FILE" )] #[derivative(Debug = "ignore")] pub private_staking_key: Option, @@ -123,7 +139,7 @@ pub struct Options { #[clap( long, env = "ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY", - conflicts_with = "key_file" + conflicts_with = "KEY_FILE" )] #[derivative(Debug = "ignore")] pub private_state_key: Option, @@ -144,21 +160,23 @@ pub struct Options { #[clap(raw = true)] modules: Vec, - /// Prefunded the builder accounts. Use for demo purposes only. - /// - /// Comma-separated list of Ethereum addresses. + /// Url we will use for RPC communication with L1. #[clap( long, - env = "ESPRESSO_SEQUENCER_PREFUNDED_BUILDER_ACCOUNTS", - value_delimiter = ',' + env = "ESPRESSO_SEQUENCER_L1_PROVIDER", + default_value = "http://localhost:8545" )] - pub prefunded_builder_accounts: Vec
, - - /// Url we will use for RPC communication with L1. - #[clap(long, env = "ESPRESSO_SEQUENCER_L1_PROVIDER")] #[derivative(Debug(format_with = "Display::fmt"))] pub l1_provider_url: Url, + /// Maximum number of L1 blocks that can be scanned for events in a single query. + #[clap( + long, + env = "ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE", + default_value = "10000" + )] + pub l1_events_max_block_range: u64, + /// Whether or not we are a DA node. #[clap(long, env = "ESPRESSO_SEQUENCER_IS_DA", action)] pub is_da: bool, @@ -168,17 +186,9 @@ pub struct Options { #[derivative(Debug(format_with = "fmt_urls"))] pub state_peers: Vec, - /// Stake table capacity for the prover circuit - #[clap(short, long, env = "ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY", default_value_t = STAKE_TABLE_CAPACITY)] - pub stake_table_capacity: usize, - - /// Maximum size in bytes of a block - #[clap(long, env = "ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE", value_parser = parse_size)] - pub max_block_size: u64, - - #[clap(long, env = "ESPRESSO_SEQUENCER_BASE_FEE")] - /// Minimum fee in WEI per byte of payload - pub base_fee: U256, + /// Exponential backoff for fetching missing state from peers. + #[clap(flatten)] + pub catchup_backoff: BackoffParams, } impl Options { @@ -240,6 +250,64 @@ pub fn parse_size(s: &str) -> Result { Ok(s.parse::()?.0) } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Ratio { + pub numerator: u64, + pub denominator: u64, +} + +impl From for (u64, u64) { + fn from(r: Ratio) -> Self { + (r.numerator, r.denominator) + } +} + +impl Display for Ratio { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}:{}", self.numerator, self.denominator) + } +} + +impl PartialOrd for Ratio { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Ratio { + fn cmp(&self, other: &Self) -> Ordering { + (self.numerator * other.denominator).cmp(&(other.numerator * self.denominator)) + } +} + +#[derive(Debug, Snafu)] +pub enum ParseRatioError { + #[snafu(display("numerator and denominator must be separated by :"))] + MissingDelimiter, + InvalidNumerator { + err: ParseIntError, + }, + InvalidDenominator { + err: ParseIntError, + }, +} + +impl FromStr for Ratio { + type Err = ParseRatioError; + + fn from_str(s: &str) -> Result { + let (num, den) = s.split_once(':').ok_or(ParseRatioError::MissingDelimiter)?; + Ok(Self { + numerator: num + .parse() + .map_err(|err| ParseRatioError::InvalidNumerator { err })?, + denominator: den + .parse() + .map_err(|err| ParseRatioError::InvalidDenominator { err })?, + }) + } +} + #[derive(Clone, Debug)] struct ModuleArgs(Vec); @@ -279,9 +347,13 @@ impl ModuleArgs { SequencerModule::Status(m) => curr = m.add(&mut modules.status, &mut provided)?, SequencerModule::State(m) => curr = m.add(&mut modules.state, &mut provided)?, SequencerModule::Catchup(m) => curr = m.add(&mut modules.catchup, &mut provided)?, + SequencerModule::Config(m) => curr = m.add(&mut modules.config, &mut provided)?, SequencerModule::HotshotEvents(m) => { curr = m.add(&mut modules.hotshot_events, &mut provided)? } + SequencerModule::Explorer(m) => { + curr = m.add(&mut modules.explorer, &mut provided)? + } } } @@ -314,7 +386,9 @@ module!("submit", api::options::Submit, requires: "http"); module!("status", api::options::Status, requires: "http"); module!("state", api::options::State, requires: "http", "storage-sql"); module!("catchup", api::options::Catchup, requires: "http"); +module!("config", api::options::Config, requires: "http"); module!("hotshot-events", api::options::HotshotEvents, requires: "http"); +module!("explorer", api::options::Explorer, requires: "http", "storage-sql"); #[derive(Clone, Debug, Args)] struct Module { @@ -384,6 +458,7 @@ enum SequencerModule { /// /// This module requires the http module to be started. Catchup(Module), + Config(Module), /// Run the merklized state API module. /// /// This module requires the http and storage-sql modules to be started. @@ -392,6 +467,10 @@ enum SequencerModule { /// /// This module requires the http module to be started. HotshotEvents(Module), + /// Run the explorer API module. + /// + /// This module requires the http and storage-sql modules to be started. + Explorer(Module), } #[derive(Clone, Debug, Default)] @@ -404,5 +483,7 @@ pub struct Modules { pub status: Option, pub state: Option, pub catchup: Option, + pub config: Option, pub hotshot_events: Option, + pub explorer: Option, } diff --git a/sequencer/src/persistence.rs b/sequencer/src/persistence.rs index c0c7f10a9..3c5470cbe 100644 --- a/sequencer/src/persistence.rs +++ b/sequencer/src/persistence.rs @@ -8,24 +8,29 @@ //! an extension that node operators can opt into. This module defines the minimum level of //! persistence which is _required_ to run a node. -use crate::{Header, Leaf, NodeState, PubKey, SeqTypes, ValidatedState, ViewNumber}; -use anyhow::{ensure, Context}; +use crate::{ + catchup::BackoffParams, ChainConfig, Leaf, NodeState, PubKey, SeqTypes, StateCatchup, + ValidatedState, ViewNumber, +}; +use anyhow::{bail, ensure, Context}; use async_std::sync::Arc; use async_trait::async_trait; -use committable::Committable; +use committable::{Commitment, Committable}; use hotshot::{ traits::ValidatedState as _, types::{Event, EventType}, HotShotInitializer, }; use hotshot_types::{ - data::{DAProposal, VidDisperseShare}, + consensus::CommitmentMap, + data::{DaProposal, QuorumProposal, VidDisperseShare}, event::{HotShotAction, LeafInfo}, message::Proposal, simple_certificate::QuorumCertificate, traits::node_implementation::ConsensusTime, + utils::View, }; -use std::cmp::max; +use std::{cmp::max, collections::BTreeMap}; pub mod fs; pub mod no_storage; @@ -34,15 +39,30 @@ pub mod sql; pub type NetworkConfig = hotshot_orchestrator::config::NetworkConfig; #[async_trait] -pub trait PersistenceOptions: Clone { +pub trait PersistenceOptions: Clone + Send + Sync + 'static { type Persistence: SequencerPersistence; async fn create(self) -> anyhow::Result; async fn reset(self) -> anyhow::Result<()>; + + async fn create_catchup_provider( + self, + backoff: BackoffParams, + ) -> anyhow::Result> { + self.create().await?.into_catchup_provider(backoff) + } } #[async_trait] -pub trait SequencerPersistence: Send + Sync + 'static { +pub trait SequencerPersistence: Sized + Send + Sync + 'static { + /// Use this storage as a state catchup backend, if supported. + fn into_catchup_provider( + self, + _backoff: BackoffParams, + ) -> anyhow::Result> { + bail!("state catchup is not implemented for this persistence type"); + } + /// Load the orchestrator config from storage. /// /// Returns `None` if no config exists (we are joining a network for the first time). Fails with @@ -71,6 +91,16 @@ pub trait SequencerPersistence: Send + Sync + 'static { async fn load_anchor_leaf(&self) -> anyhow::Result)>>; + /// Load undecided state saved by consensus before we shut down. + async fn load_undecided_state( + &self, + ) -> anyhow::Result, BTreeMap>)>>; + + /// Load the proposals saved by consensus + async fn load_quorum_proposals( + &self, + ) -> anyhow::Result>>>>; + async fn load_vid_share( &self, view: ViewNumber, @@ -78,10 +108,7 @@ pub trait SequencerPersistence: Send + Sync + 'static { async fn load_da_proposal( &self, view: ViewNumber, - ) -> anyhow::Result>>>; - - /// Load the validated state after `header`, if available. - async fn load_validated_state(&self, header: &Header) -> anyhow::Result; + ) -> anyhow::Result>>>; /// Load the latest known consensus state. /// @@ -91,6 +118,7 @@ pub trait SequencerPersistence: Send + Sync + 'static { &self, state: NodeState, ) -> anyhow::Result> { + let genesis_validated_state = ValidatedState::genesis(&state).0; let highest_voted_view = match self .load_latest_acted_view() .await @@ -105,7 +133,7 @@ pub trait SequencerPersistence: Send + Sync + 'static { ViewNumber::genesis() } }; - let (leaf, high_qc, validated_state) = match self + let (leaf, high_qc) = match self .load_anchor_leaf() .await .context("loading anchor leaf")? @@ -113,52 +141,73 @@ pub trait SequencerPersistence: Send + Sync + 'static { Some((leaf, high_qc)) => { tracing::info!(?leaf, ?high_qc, "starting from saved leaf"); ensure!( - leaf.get_view_number() == high_qc.view_number, + leaf.view_number() == high_qc.view_number, format!( "loaded anchor leaf from view {:?}, but high QC is from view {:?}", - leaf.get_view_number(), + leaf.view_number(), high_qc.view_number ) ); - - let validated_state = match self.load_validated_state(leaf.get_block_header()).await - { - Ok(validated_state) => Some(Arc::new(validated_state)), - Err(err) => { - tracing::error!( - "unable to load validated state, will need to catchup: {err:#}" - ); - None - } - }; - - (leaf, high_qc, validated_state) + (leaf, high_qc) } None => { tracing::info!("no saved leaf, starting from genesis leaf"); ( - Leaf::genesis(&state), - QuorumCertificate::genesis(&state), - Some(Arc::new(ValidatedState::genesis(&state).0)), + Leaf::genesis(&genesis_validated_state, &state).await, + QuorumCertificate::genesis(&genesis_validated_state, &state).await, ) } }; + let validated_state = if leaf.block_header().height == 0 { + // If we are starting from genesis, we can provide the full state. + Some(Arc::new(genesis_validated_state)) + } else { + // Otherwise, we will have to construct a sparse state and fetch missing data during + // catchup. + None + }; + + // If we are not starting from genesis, we start from the view following the maximum view + // between `highest_voted_view` and `leaf.view_number`. This prevents double votes from + // starting in a view in which we had already voted before the restart, and prevents + // unnecessary catchup from starting in a view earlier than the anchor leaf. + let mut view = max(highest_voted_view, leaf.view_number()); + if view != ViewNumber::genesis() { + view += 1; + } - // We start from the view following the maximum view between `highest_voted_view` and - // `leaf.view_number`. This prevents double votes from starting in a view in which we had - // already voted before the restart, and prevents unnecessary catchup from starting in a - // view earlier than the anchor leaf. - let view = max(highest_voted_view, leaf.get_view_number()) + 1; + let (undecided_leaves, undecided_state) = self + .load_undecided_state() + .await + .context("loading undecided state")? + .unwrap_or_default(); - tracing::info!(?leaf, ?view, ?high_qc, "loaded consensus state"); + let saved_proposals = self + .load_quorum_proposals() + .await + .context("loading saved proposals") + .unwrap_or_default() + .unwrap_or_default(); + + tracing::info!( + ?leaf, + ?view, + ?high_qc, + ?validated_state, + ?undecided_leaves, + ?undecided_state, + ?saved_proposals, + "loaded consensus state" + ); Ok(HotShotInitializer::from_reload( leaf, state, validated_state, view, + saved_proposals, high_qc, - Default::default(), - Default::default(), + undecided_leaves.into_values().collect(), + undecided_state, )) } @@ -166,9 +215,9 @@ pub trait SequencerPersistence: Send + Sync + 'static { async fn handle_event(&mut self, event: &Event) { if let EventType::Decide { leaf_chain, qc, .. } = &event.event { if let Some(LeafInfo { leaf, .. }) = leaf_chain.first() { - if qc.view_number != leaf.get_view_number() { + if qc.view_number != leaf.view_number() { tracing::error!( - leaf_view = ?leaf.get_view_number(), + leaf_view = ?leaf.view_number(), qc_view = ?qc.view_number, "latest leaf and QC are from different views!", ); @@ -182,7 +231,7 @@ pub trait SequencerPersistence: Send + Sync + 'static { ); } - if let Err(err) = self.collect_garbage(leaf.get_view_number()).await { + if let Err(err) = self.collect_garbage(leaf.view_number()).await { tracing::error!("Failed to garbage collect. {err:#}",); } } @@ -195,17 +244,36 @@ pub trait SequencerPersistence: Send + Sync + 'static { ) -> anyhow::Result<()>; async fn append_da( &mut self, - proposal: &Proposal>, + proposal: &Proposal>, ) -> anyhow::Result<()>; async fn record_action( &mut self, view: ViewNumber, action: HotShotAction, ) -> anyhow::Result<()>; + async fn update_undecided_state( + &mut self, + leaves: CommitmentMap, + state: BTreeMap>, + ) -> anyhow::Result<()>; + async fn append_quorum_proposal( + &mut self, + proposal: &Proposal>, + ) -> anyhow::Result<()>; +} + +#[async_trait] +pub trait ChainConfigPersistence: Sized + Send + Sync + 'static { + async fn insert_chain_config(&mut self, chain_config: ChainConfig) -> anyhow::Result<()>; + async fn load_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result; } #[cfg(test)] mod testing { + use super::*; #[async_trait] @@ -222,15 +290,14 @@ mod testing { mod persistence_tests { use super::*; - use crate::{NodeState, Transaction}; + use crate::NodeState; use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; + use hotshot::types::BLSPubKey; use hotshot::types::SignatureKey; - use hotshot::{traits::BlockPayload, types::BLSPubKey}; + use hotshot_types::traits::EncodeBytes; use hotshot_types::{event::HotShotAction, vid::vid_scheme}; - use jf_primitives::vid::VidScheme; - use rand::{RngCore, SeedableRng}; - use sha2::{Digest, Sha256}; + use jf_vid::VidScheme; use testing::TestablePersistence; #[async_std::test] @@ -245,8 +312,8 @@ mod persistence_tests { assert_eq!(storage.load_anchor_leaf().await.unwrap(), None); // Store a leaf. - let leaf1 = Leaf::genesis(&NodeState::mock()); - let qc1 = QuorumCertificate::genesis(&NodeState::mock()); + let leaf1 = Leaf::genesis(&ValidatedState::default(), &NodeState::mock()).await; + let qc1 = QuorumCertificate::genesis(&ValidatedState::default(), &NodeState::mock()).await; storage.save_anchor_leaf(&leaf1, &qc1).await.unwrap(); assert_eq!( storage.load_anchor_leaf().await.unwrap().unwrap(), @@ -255,7 +322,7 @@ mod persistence_tests { // Store a newer leaf, make sure storage gets updated. let mut leaf2 = leaf1.clone(); - leaf2.get_block_header_mut().height += 1; + leaf2.block_header_mut().height += 1; let mut qc2 = qc1.clone(); qc2.data.leaf_commit = leaf2.commit(); qc2.vote_commitment = qc2.data.commit(); @@ -331,11 +398,14 @@ mod persistence_tests { None ); - let leaf = Leaf::genesis(&NodeState::mock()); - let payload = leaf.get_block_payload().unwrap(); - let bytes = payload.encode().unwrap().to_vec(); - let disperse = vid_scheme(2).disperse(bytes).unwrap(); + let leaf = Leaf::genesis(&ValidatedState::default(), &NodeState::mock()).await; + let leaf_payload = leaf.block_payload().unwrap(); + let leaf_payload_bytes_arc = leaf_payload.encode(); + let disperse = vid_scheme(2) + .disperse(leaf_payload_bytes_arc.clone()) + .unwrap(); let (pubkey, privkey) = BLSPubKey::generated_from_seed_indexed([0; 32], 1); + let signature = PubKey::sign(&privkey, &[]).unwrap(); let mut vid = VidDisperseShare:: { view_number: ViewNumber::new(1), payload_commitment: Default::default(), @@ -343,6 +413,21 @@ mod persistence_tests { common: disperse.common, recipient_key: pubkey, }; + let mut quorum_proposal = Proposal { + data: QuorumProposal:: { + block_header: leaf.block_header().clone(), + view_number: ViewNumber::genesis(), + justify_qc: QuorumCertificate::genesis( + &ValidatedState::default(), + &NodeState::mock(), + ) + .await, + upgrade_certificate: None, + proposal_certificate: None, + }, + signature, + _pd: Default::default(), + }; let vid_share1 = vid.clone().to_proposal(&privkey).unwrap().clone(); @@ -373,18 +458,12 @@ mod persistence_tests { Some(vid_share3.clone()) ); - let mut seed = [0u8; 32]; - let mut rng = rand_chacha::ChaChaRng::from_entropy(); - rng.fill_bytes(&mut seed); - - let tx = Transaction::random(&mut rng); - let tx_hash = Sha256::digest(tx.payload()).to_vec(); - let block_payload_signature = - BLSPubKey::sign(&privkey, &tx_hash).expect("Failed to sign tx hash"); + let block_payload_signature = BLSPubKey::sign(&privkey, &leaf_payload_bytes_arc) + .expect("Failed to sign block payload"); - let da_proposal_inner = DAProposal:: { - encoded_transactions: Arc::from(tx_hash), - metadata: Default::default(), + let da_proposal_inner = DaProposal:: { + encoded_transactions: leaf_payload_bytes_arc, + metadata: leaf_payload.ns_table().clone(), view_number: ViewNumber::new(1), }; @@ -425,6 +504,70 @@ mod persistence_tests { Some(da_proposal3.clone()) ); + let quorum_proposal1 = quorum_proposal.clone(); + storage + .append_quorum_proposal(&quorum_proposal1) + .await + .unwrap(); + + assert_eq!( + storage.load_quorum_proposals().await.unwrap(), + Some(BTreeMap::from_iter([( + ViewNumber::genesis(), + quorum_proposal1.clone() + )])) + ); + + quorum_proposal.data.view_number = ViewNumber::new(1); + let quorum_proposal2 = quorum_proposal.clone(); + storage + .append_quorum_proposal(&quorum_proposal2) + .await + .unwrap(); + + assert_eq!( + storage.load_quorum_proposals().await.unwrap(), + Some(BTreeMap::from_iter([ + (ViewNumber::genesis(), quorum_proposal1.clone()), + (ViewNumber::new(1), quorum_proposal2.clone()) + ])) + ); + + quorum_proposal.data.view_number = ViewNumber::new(2); + let quorum_proposal3 = quorum_proposal.clone(); + storage + .append_quorum_proposal(&quorum_proposal3) + .await + .unwrap(); + + assert_eq!( + storage.load_quorum_proposals().await.unwrap(), + Some(BTreeMap::from_iter([ + (ViewNumber::genesis(), quorum_proposal1.clone()), + (ViewNumber::new(1), quorum_proposal2.clone()), + (ViewNumber::new(2), quorum_proposal3.clone()) + ])) + ); + + quorum_proposal.data.view_number = ViewNumber::new(10); + + // This one should stick around after GC runs. + let quorum_proposal4 = quorum_proposal.clone(); + storage + .append_quorum_proposal(&quorum_proposal4) + .await + .unwrap(); + + assert_eq!( + storage.load_quorum_proposals().await.unwrap(), + Some(BTreeMap::from_iter([ + (ViewNumber::genesis(), quorum_proposal1), + (ViewNumber::new(1), quorum_proposal2), + (ViewNumber::new(2), quorum_proposal3), + (ViewNumber::new(10), quorum_proposal4.clone()) + ])) + ); + // Test garbage collection // Deleting da proposals and vid shares with view number <=2 storage.collect_garbage(ViewNumber::new(2)).await.unwrap(); @@ -450,5 +593,14 @@ mod persistence_tests { storage.load_vid_share(ViewNumber::new(3)).await.unwrap(), Some(vid_share3) ); + + let proposals = storage.load_quorum_proposals().await.unwrap(); + assert_eq!( + proposals, + Some(BTreeMap::from_iter([( + ViewNumber::new(10), + quorum_proposal4 + )])) + ) } } diff --git a/sequencer/src/persistence/fs.rs b/sequencer/src/persistence/fs.rs index 6f413d529..a238a3da1 100644 --- a/sequencer/src/persistence/fs.rs +++ b/sequencer/src/persistence/fs.rs @@ -1,18 +1,21 @@ use super::{NetworkConfig, PersistenceOptions, SequencerPersistence}; -use crate::{Header, Leaf, SeqTypes, ValidatedState, ViewNumber}; -use anyhow::{anyhow, bail, Context}; +use crate::{Leaf, SeqTypes, ViewNumber}; +use anyhow::{anyhow, Context}; use async_trait::async_trait; use clap::Parser; use hotshot_types::{ - data::{DAProposal, VidDisperseShare}, + consensus::CommitmentMap, + data::{DaProposal, QuorumProposal, VidDisperseShare}, event::HotShotAction, message::Proposal, simple_certificate::QuorumCertificate, traits::node_implementation::ConsensusTime, + utils::View, vote::HasViewNumber, }; use std::{ + collections::BTreeMap, fs::{self, File, OpenOptions}, io::{Read, Seek, SeekFrom, Write}, path::{Path, PathBuf}, @@ -23,7 +26,10 @@ use std::{ pub struct Options { /// Storage path for persistent data. #[clap(long, env = "ESPRESSO_SEQUENCER_STORAGE_PATH")] - pub path: PathBuf, + path: PathBuf, + + #[clap(long, env = "ESPRESSO_SEQUENCER_STORE_UNDECIDED_STATE", hide = true)] + store_undecided_state: bool, } impl Default for Options { @@ -32,12 +38,28 @@ impl Default for Options { } } +impl Options { + pub fn new(path: PathBuf) -> Self { + Self { + path, + store_undecided_state: false, + } + } + + pub(crate) fn path(&self) -> &Path { + &self.path + } +} + #[async_trait] impl PersistenceOptions for Options { type Persistence = Persistence; async fn create(self) -> anyhow::Result { - Ok(Persistence(self.path)) + Ok(Persistence { + path: self.path, + store_undecided_state: self.store_undecided_state, + }) } async fn reset(self) -> anyhow::Result<()> { @@ -47,27 +69,38 @@ impl PersistenceOptions for Options { /// File system backed persistence. #[derive(Clone, Debug)] -pub struct Persistence(PathBuf); +pub struct Persistence { + path: PathBuf, + store_undecided_state: bool, +} impl Persistence { fn config_path(&self) -> PathBuf { - self.0.join("hotshot.cfg") + self.path.join("hotshot.cfg") } fn voted_view_path(&self) -> PathBuf { - self.0.join("highest_voted_view") + self.path.join("highest_voted_view") } fn anchor_leaf_path(&self) -> PathBuf { - self.0.join("anchor_leaf") + self.path.join("anchor_leaf") } fn vid_dir_path(&self) -> PathBuf { - self.0.join("vid") + self.path.join("vid") } fn da_dir_path(&self) -> PathBuf { - self.0.join("da") + self.path.join("da") + } + + fn undecided_state_path(&self) -> PathBuf { + self.path.join("undecided_state") + } + + fn quorum_proposals_dir_path(&self) -> PathBuf { + self.path.join("quorum_proposals") } /// Overwrite a file if a condition is met. @@ -134,7 +167,7 @@ impl SequencerPersistence for Persistence { } async fn collect_garbage(&mut self, view: ViewNumber) -> anyhow::Result<()> { - let view_number = view.get_u64(); + let view_number = view.u64(); let delete_files = |dir_path: PathBuf| -> anyhow::Result<()> { if !dir_path.is_dir() { @@ -158,7 +191,8 @@ impl SequencerPersistence for Persistence { }; delete_files(self.da_dir_path())?; - delete_files(self.vid_dir_path()) + delete_files(self.vid_dir_path())?; + delete_files(self.quorum_proposals_dir_path()) } async fn load_latest_acted_view(&self) -> anyhow::Result> { @@ -191,10 +225,10 @@ impl SequencerPersistence for Persistence { let mut height_bytes = [0; 8]; file.read_exact(&mut height_bytes).context("read height")?; let height = u64::from_le_bytes(height_bytes); - if height >= leaf.get_height() { + if height >= leaf.height() { tracing::warn!( saved_height = height, - new_height = leaf.get_height(), + new_height = leaf.height(), "not writing anchor leaf because saved leaf has newer height", ); return Ok(false); @@ -206,7 +240,7 @@ impl SequencerPersistence for Persistence { }, |mut file| { // Save the new leaf. First we write the height. - file.write_all(&leaf.get_height().to_le_bytes()) + file.write_all(&leaf.height().to_le_bytes()) .context("write height")?; // Now serialize and write out the actual leaf and its corresponding QC. let bytes = bincode::serialize(&(leaf, qc)).context("serialize leaf")?; @@ -234,15 +268,24 @@ impl SequencerPersistence for Persistence { Ok(Some(bincode::deserialize(&bytes).context("deserialize")?)) } + async fn load_undecided_state( + &self, + ) -> anyhow::Result, BTreeMap>)>> { + let path = self.undecided_state_path(); + if !path.is_file() { + return Ok(None); + } + let bytes = fs::read(&path).context("read")?; + Ok(Some(bincode::deserialize(&bytes).context("deserialize")?)) + } + async fn load_da_proposal( &self, view: ViewNumber, - ) -> anyhow::Result>>> { + ) -> anyhow::Result>>> { let dir_path = self.da_dir_path(); - let file_path = dir_path - .join(view.get_u64().to_string()) - .with_extension("txt"); + let file_path = dir_path.join(view.u64().to_string()).with_extension("txt"); if !file_path.exists() { return Ok(None); @@ -250,7 +293,7 @@ impl SequencerPersistence for Persistence { let da_bytes = fs::read(file_path)?; - let da_proposal: Proposal> = + let da_proposal: Proposal> = bincode::deserialize(&da_bytes)?; Ok(Some(da_proposal)) } @@ -261,9 +304,7 @@ impl SequencerPersistence for Persistence { ) -> anyhow::Result>>> { let dir_path = self.vid_dir_path(); - let file_path = dir_path - .join(view.get_u64().to_string()) - .with_extension("txt"); + let file_path = dir_path.join(view.u64().to_string()).with_extension("txt"); if !file_path.exists() { return Ok(None); @@ -279,7 +320,7 @@ impl SequencerPersistence for Persistence { &mut self, proposal: &Proposal>, ) -> anyhow::Result<()> { - let view_number = proposal.data.get_view_number().get_u64(); + let view_number = proposal.data.view_number().u64(); let dir_path = self.vid_dir_path(); fs::create_dir_all(dir_path.clone()).context("failed to create vid dir")?; @@ -302,9 +343,9 @@ impl SequencerPersistence for Persistence { } async fn append_da( &mut self, - proposal: &Proposal>, + proposal: &Proposal>, ) -> anyhow::Result<()> { - let view_number = proposal.data.get_view_number().get_u64(); + let view_number = proposal.data.view_number().u64(); let dir_path = self.da_dir_path(); fs::create_dir_all(dir_path.clone()).context("failed to create da dir")?; @@ -344,14 +385,112 @@ impl SequencerPersistence for Persistence { Ok(saved_view < view) }, |mut file| { - file.write_all(&view.get_u64().to_le_bytes())?; + file.write_all(&view.u64().to_le_bytes())?; + Ok(()) + }, + ) + } + async fn update_undecided_state( + &mut self, + leaves: CommitmentMap, + state: BTreeMap>, + ) -> anyhow::Result<()> { + if !self.store_undecided_state { + return Ok(()); + } + + self.replace( + &self.undecided_state_path(), + |_| { + // Always overwrite the previous file. + Ok(true) + }, + |mut file| { + let bytes = + bincode::serialize(&(leaves, state)).context("serializing undecided state")?; + file.write_all(&bytes)?; + Ok(()) + }, + ) + } + async fn append_quorum_proposal( + &mut self, + proposal: &Proposal>, + ) -> anyhow::Result<()> { + let view_number = proposal.data.view_number().u64(); + let dir_path = self.quorum_proposals_dir_path(); + + fs::create_dir_all(dir_path.clone()).context("failed to create proposals dir")?; + + let file_path = dir_path.join(view_number.to_string()).with_extension("txt"); + self.replace( + &file_path, + |_| { + // Always overwrite the previous file + Ok(true) + }, + |mut file| { + let proposal_bytes = bincode::serialize(&proposal).context("serialize proposal")?; + + file.write_all(&proposal_bytes)?; Ok(()) }, ) } + async fn load_quorum_proposals( + &self, + ) -> anyhow::Result>>>> + { + // First, get the proposal directory. + let dir_path = self.quorum_proposals_dir_path(); + + // Then, we want to get the entries in this directory since they'll be the + // key/value pairs for our map. + let files: Vec = fs::read_dir(dir_path.clone())? + .filter_map(|entry| { + entry + .ok() + .filter(|e| e.file_type().map(|ft| ft.is_file()).unwrap_or(false)) + }) + .collect(); + + // Do we have any entries? + if files.is_empty() { + // Don't bother continuing if we don't have any data. + return Ok(None); + } + + // Read all of the files + let proposal_files = files + .into_iter() + .map(|entry| dir_path.join(entry.file_name()).with_extension("txt")); + + let mut map = BTreeMap::new(); + for file in proposal_files.into_iter() { + // This operation shouldn't fail, but we don't want to panic here if the filesystem + // somehow gets corrupted. We get the stem to remove the ".txt" from the end. + if let Some(file_name) = file.file_stem() { + // We need to convert the filename (which corresponds to the view) + let view_number = ViewNumber::new( + file_name + .to_string_lossy() + .parse::() + .context("convert file name to u64")?, + ); + + // Now, we'll try and load the proposal associated with this function. + let proposal_bytes = fs::read(file)?; + + // Then, deserialize. + let proposal: Proposal> = + bincode::deserialize(&proposal_bytes)?; + + // Push to the map and we're done. + map.insert(view_number, proposal); + } + } - async fn load_validated_state(&self, _header: &Header) -> anyhow::Result { - bail!("state persistence not implemented"); + Ok(Some(map)) } } @@ -370,7 +509,7 @@ mod testing { } async fn connect(storage: &Self::Storage) -> Self { - Persistence(storage.path().into()) + Options::new(storage.path().into()).create().await.unwrap() } } } diff --git a/sequencer/src/persistence/no_storage.rs b/sequencer/src/persistence/no_storage.rs index f5d73a373..9a39296eb 100644 --- a/sequencer/src/persistence/no_storage.rs +++ b/sequencer/src/persistence/no_storage.rs @@ -2,15 +2,17 @@ #![cfg(any(test, feature = "testing"))] use super::{NetworkConfig, PersistenceOptions, SequencerPersistence}; -use crate::{Header, Leaf, SeqTypes, ValidatedState, ViewNumber}; -use anyhow::bail; +use crate::{Leaf, SeqTypes, ViewNumber}; use async_trait::async_trait; use hotshot_types::{ - data::{DAProposal, VidDisperseShare}, + consensus::CommitmentMap, + data::{DaProposal, QuorumProposal, VidDisperseShare}, event::HotShotAction, message::Proposal, simple_certificate::QuorumCertificate, + utils::View, }; +use std::collections::BTreeMap; #[derive(Clone, Copy, Debug)] pub struct Options; @@ -63,10 +65,16 @@ impl SequencerPersistence for NoStorage { Ok(None) } + async fn load_undecided_state( + &self, + ) -> anyhow::Result, BTreeMap>)>> { + Ok(None) + } + async fn load_da_proposal( &self, _view: ViewNumber, - ) -> anyhow::Result>>> { + ) -> anyhow::Result>>> { Ok(None) } @@ -77,6 +85,13 @@ impl SequencerPersistence for NoStorage { Ok(None) } + async fn load_quorum_proposals( + &self, + ) -> anyhow::Result>>>> + { + Ok(None) + } + async fn append_vid( &mut self, _proposal: &Proposal>, @@ -85,7 +100,7 @@ impl SequencerPersistence for NoStorage { } async fn append_da( &mut self, - _proposal: &Proposal>, + _proposal: &Proposal>, ) -> anyhow::Result<()> { Ok(()) } @@ -96,8 +111,17 @@ impl SequencerPersistence for NoStorage { ) -> anyhow::Result<()> { Ok(()) } - - async fn load_validated_state(&self, _header: &Header) -> anyhow::Result { - bail!("state persistence not implemented"); + async fn update_undecided_state( + &mut self, + _leaves: CommitmentMap, + _state: BTreeMap>, + ) -> anyhow::Result<()> { + Ok(()) + } + async fn append_quorum_proposal( + &mut self, + _proposal: &Proposal>, + ) -> anyhow::Result<()> { + Ok(()) } } diff --git a/sequencer/src/persistence/sql.rs b/sequencer/src/persistence/sql.rs index 2b8602cd4..7446bfd6f 100644 --- a/sequencer/src/persistence/sql.rs +++ b/sequencer/src/persistence/sql.rs @@ -1,36 +1,36 @@ use super::{NetworkConfig, PersistenceOptions, SequencerPersistence}; use crate::{ + catchup::{BackoffParams, SqlStateCatchup, StateCatchup}, options::parse_duration, - state::{BlockMerkleTree, FeeMerkleTree}, - Header, Leaf, SeqTypes, ValidatedState, ViewNumber, + Leaf, SeqTypes, ViewNumber, +}; +use anyhow::Context; +use async_std::{ + stream::StreamExt, + sync::{Arc, RwLock}, }; -use anyhow::{bail, Context}; use async_trait::async_trait; use clap::Parser; use derivative::Derivative; use futures::future::{BoxFuture, FutureExt}; -use hotshot_query_service::{ - data_source::{ - storage::{ - pruning::PrunerCfg, - sql::{ - include_migrations, postgres::types::ToSql, Config, Query, SqlStorage, Transaction, - }, - }, - VersionedDataSource, +use hotshot_query_service::data_source::{ + storage::{ + pruning::PrunerCfg, + sql::{include_migrations, postgres::types::ToSql, Config, Query, SqlStorage, Transaction}, }, - merklized_state::{MerklizedState, MerklizedStateDataSource, Snapshot}, + VersionedDataSource, }; use hotshot_types::{ - data::{DAProposal, VidDisperseShare}, + consensus::CommitmentMap, + data::{DaProposal, QuorumProposal, VidDisperseShare}, event::HotShotAction, message::Proposal, simple_certificate::QuorumCertificate, traits::node_implementation::ConsensusTime, + utils::View, vote::HasViewNumber, }; -use jf_primitives::merkle_tree::{ForgetableMerkleTreeScheme, MerkleTreeScheme}; -use std::time::Duration; +use std::{collections::BTreeMap, time::Duration}; /// Options for Postgres-backed persistence. #[derive(Parser, Clone, Derivative, Default)] @@ -48,33 +48,33 @@ pub struct Options { /// addition, there are some parameters which cannot be set via the URI, such as TLS. // Hide from debug output since may contain sensitive data. #[derivative(Debug = "ignore")] - pub uri: Option, + pub(crate) uri: Option, /// Hostname for the remote Postgres database server. #[clap(long, env = "ESPRESSO_SEQUENCER_POSTGRES_HOST")] - pub host: Option, + pub(crate) host: Option, /// Port for the remote Postgres database server. #[clap(long, env = "ESPRESSO_SEQUENCER_POSTGRES_PORT")] - pub port: Option, + pub(crate) port: Option, /// Name of database to connect to. #[clap(long, env = "ESPRESSO_SEQUENCER_POSTGRES_DATABASE")] - pub database: Option, + pub(crate) database: Option, /// Postgres user to connect as. #[clap(long, env = "ESPRESSO_SEQUENCER_POSTGRES_USER")] - pub user: Option, + pub(crate) user: Option, /// Password for Postgres user. #[clap(long, env = "ESPRESSO_SEQUENCER_POSTGRES_PASSWORD")] // Hide from debug output since may contain sensitive data. #[derivative(Debug = "ignore")] - pub password: Option, + pub(crate) password: Option, /// Use TLS for an encrypted connection to the database. #[clap(long, env = "ESPRESSO_SEQUENCER_POSTGRES_USE_TLS")] - pub use_tls: bool, + pub(crate) use_tls: bool, /// This will enable the pruner and set the default pruning parameters unless provided. /// Default parameters: @@ -85,11 +85,35 @@ pub struct Options { /// - max_usage: 80% /// - interval: 1 hour #[clap(long, env = "ESPRESSO_SEQUENCER_POSTGRES_PRUNE")] - pub prune: bool, + pub(crate) prune: bool, /// Pruning parameters. #[clap(flatten)] - pub pruning: PruningOptions, + pub(crate) pruning: PruningOptions, + + #[clap(long, env = "ESPRESSO_SEQUENCER_STORE_UNDECIDED_STATE", hide = true)] + pub(crate) store_undecided_state: bool, + + /// Specifies the maximum number of concurrent fetch requests allowed from peers. + #[clap(long, env = "ESPRESSO_SEQUENCER_FETCH_RATE_LIMIT")] + pub(crate) fetch_rate_limit: Option, + + /// The minimum delay between active fetches in a stream. + #[clap(long, env = "ESPRESSO_SEQUENCER_ACTIVE_FETCH_DELAY", value_parser = parse_duration)] + pub(crate) active_fetch_delay: Option, + + /// The minimum delay between loading chunks in a stream. + #[clap(long, env = "ESPRESSO_SEQUENCER_CHUNK_FETCH_DELAY", value_parser = parse_duration)] + pub(crate) chunk_fetch_delay: Option, + + /// Disable pruning and reconstruct previously pruned data. + /// + /// While running without pruning is the default behavior, the default will not try to + /// reconstruct data that was pruned in a previous run where pruning was enabled. This option + /// instructs the service to run without pruning _and_ reconstruct all previously pruned data by + /// fetching from peers. + #[clap(long, env = "ESPRESSO_SEQUENCER_ARCHIVE", conflicts_with = "prune")] + pub(crate) archive: bool, } impl TryFrom for Config { @@ -124,6 +148,9 @@ impl TryFrom for Config { if opt.prune { cfg = cfg.pruner_cfg(PrunerCfg::from(opt.pruning))?; } + if opt.archive { + cfg = cfg.archive(); + } Ok(cfg) } @@ -208,7 +235,10 @@ impl PersistenceOptions for Options { type Persistence = Persistence; async fn create(self) -> anyhow::Result { - SqlStorage::connect(self.try_into()?).await + Ok(Persistence { + store_undecided_state: self.store_undecided_state, + db: SqlStorage::connect(self.try_into()?).await?, + }) } async fn reset(self) -> anyhow::Result<()> { @@ -218,21 +248,30 @@ impl PersistenceOptions for Options { } /// Postgres-backed persistence. -pub type Persistence = SqlStorage; +pub struct Persistence { + db: SqlStorage, + store_undecided_state: bool, +} -async fn transaction( - db: &mut Persistence, +pub(crate) async fn transaction( + sql: &mut SqlStorage, f: impl FnOnce(Transaction) -> BoxFuture>, ) -> anyhow::Result<()> { - let tx = db.transaction().await?; + let tx = sql.transaction().await?; match f(tx).await { Ok(_) => { - db.commit().await?; + if let Err(err) = sql.commit().await { + tracing::warn!("transaction failed, reverting: {err:#}"); + sql.revert().await; + + return Err(err.into()); + } + Ok(()) } Err(err) => { tracing::warn!("transaction failed, reverting: {err:#}"); - db.revert().await; + sql.revert().await; Err(err) } } @@ -240,11 +279,22 @@ async fn transaction( #[async_trait] impl SequencerPersistence for Persistence { + fn into_catchup_provider( + self, + backoff: BackoffParams, + ) -> anyhow::Result> { + Ok(Arc::new(SqlStateCatchup::new( + Arc::new(RwLock::new(self.db)), + backoff, + ))) + } + async fn load_config(&self) -> anyhow::Result> { tracing::info!("loading config from Postgres"); // Select the most recent config (although there should only be one). let Some(row) = self + .db .query_opt_static("SELECT config FROM network_config ORDER BY id DESC LIMIT 1") .await? else { @@ -259,7 +309,7 @@ impl SequencerPersistence for Persistence { tracing::info!("saving config to Postgres"); let json = serde_json::to_value(cfg)?; - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.execute_one_with_retries( "INSERT INTO network_config (config) VALUES ($1)", @@ -274,13 +324,16 @@ impl SequencerPersistence for Persistence { } async fn collect_garbage(&mut self, view: ViewNumber) -> anyhow::Result<()> { - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { let stmt1 = "DELETE FROM vid_share where view <= $1"; - tx.execute(stmt1, [&(view.get_u64() as i64)]).await?; + tx.execute(stmt1, [&(view.u64() as i64)]).await?; let stmt2 = "DELETE FROM da_proposal where view <= $1"; - tx.execute(stmt2, [&(view.get_u64() as i64)]).await?; + tx.execute(stmt2, [&(view.u64() as i64)]).await?; + + let stmt3 = "DELETE FROM quorum_proposals where view <= $1"; + tx.execute(stmt3, [&(view.u64() as i64)]).await?; Ok(()) } .boxed() @@ -312,12 +365,12 @@ impl SequencerPersistence for Persistence { ) "; - let height = leaf.get_height() as i64; - let view = qc.view_number.get_u64() as i64; + let height = leaf.height() as i64; + let view = qc.view_number.u64() as i64; let leaf_bytes = bincode::serialize(leaf)?; let qc_bytes = bincode::serialize(qc)?; - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.execute_one_with_retries( stmt, @@ -338,6 +391,7 @@ impl SequencerPersistence for Persistence { async fn load_latest_acted_view(&self) -> anyhow::Result> { Ok(self + .db .query_opt_static("SELECT view FROM highest_voted_view WHERE id = 0") .await? .map(|row| { @@ -350,6 +404,7 @@ impl SequencerPersistence for Persistence { &self, ) -> anyhow::Result)>> { let Some(row) = self + .db .query_opt_static("SELECT leaf, qc FROM anchor_leaf WHERE id = 0") .await? else { @@ -365,14 +420,35 @@ impl SequencerPersistence for Persistence { Ok(Some((leaf, qc))) } + async fn load_undecided_state( + &self, + ) -> anyhow::Result, BTreeMap>)>> { + let Some(row) = self + .db + .query_opt_static("SELECT leaves, state FROM undecided_state WHERE id = 0") + .await? + else { + return Ok(None); + }; + + let leaves_bytes: Vec = row.get("leaves"); + let leaves = bincode::deserialize(&leaves_bytes)?; + + let state_bytes: Vec = row.get("state"); + let state = bincode::deserialize(&state_bytes)?; + + Ok(Some((leaves, state))) + } + async fn load_da_proposal( &self, view: ViewNumber, - ) -> anyhow::Result>>> { + ) -> anyhow::Result>>> { let result = self + .db .query_opt( "SELECT data FROM da_proposal where view = $1", - [&(view.get_u64() as i64)], + [&(view.u64() as i64)], ) .await?; @@ -389,9 +465,10 @@ impl SequencerPersistence for Persistence { view: ViewNumber, ) -> anyhow::Result>>> { let result = self + .db .query_opt( "SELECT data FROM vid_share where view = $1", - [&(view.get_u64() as i64)], + [&(view.u64() as i64)], ) .await?; @@ -403,15 +480,39 @@ impl SequencerPersistence for Persistence { .transpose() } + async fn load_quorum_proposals( + &self, + ) -> anyhow::Result>>>> + { + let rows = self + .db + .query_static("SELECT * FROM quorum_proposals") + .await?; + + Ok(Some(BTreeMap::from_iter( + rows.map(|row| { + let row = row?; + let view: i64 = row.get("view"); + let view_number: ViewNumber = ViewNumber::new(view.try_into()?); + let bytes: Vec = row.get("data"); + let proposal: Proposal> = + bincode::deserialize(&bytes)?; + Ok((view_number, proposal)) + }) + .collect::>>() + .await?, + ))) + } + async fn append_vid( &mut self, proposal: &Proposal>, ) -> anyhow::Result<()> { let data = &proposal.data; - let view = data.get_view_number().get_u64(); + let view = data.view_number().u64(); let data_bytes = bincode::serialize(proposal).unwrap(); - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.upsert( "vid_share", @@ -428,13 +529,13 @@ impl SequencerPersistence for Persistence { } async fn append_da( &mut self, - proposal: &Proposal>, + proposal: &Proposal>, ) -> anyhow::Result<()> { let data = &proposal.data; - let view = data.get_view_number().get_u64(); + let view = data.view_number().u64(); let data_bytes = bincode::serialize(proposal).unwrap(); - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.upsert( "da_proposal", @@ -458,9 +559,9 @@ impl SequencerPersistence for Persistence { INSERT INTO highest_voted_view (id, view) VALUES (0, $1) ON CONFLICT (id) DO UPDATE SET view = GREATEST(highest_voted_view.view, excluded.view)"; - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { - tx.execute_one_with_retries(stmt, [view.get_u64() as i64]) + tx.execute_one_with_retries(stmt, [view.u64() as i64]) .await?; Ok(()) } @@ -468,45 +569,61 @@ impl SequencerPersistence for Persistence { }) .await } + async fn update_undecided_state( + &mut self, + leaves: CommitmentMap, + state: BTreeMap>, + ) -> anyhow::Result<()> { + if !self.store_undecided_state { + return Ok(()); + } - async fn load_validated_state(&self, header: &Header) -> anyhow::Result { - let height = header.height; - let block_merkle_tree = if height == 0 { - BlockMerkleTree::new(BlockMerkleTree::tree_height()) - } else { - // For the block Merkle tree, we only require the frontier, which we can load from - // storage and remember into a sparse tree. - let mut tree = BlockMerkleTree::from_commitment(header.block_merkle_tree_root); - let snapshot = - Snapshot::<_, BlockMerkleTree, { BlockMerkleTree::ARITY }>::Index(height); - let frontier = self - .get_path(snapshot, height - 1) - .await - .context("fetching frontier")?; - let Some(&parent) = frontier.elem() else { - bail!("invalid frontier: missing element"); - }; - tree.remember(height - 1, parent, frontier) - .context("remembering frontier")?; - tree - }; - - // For the fee Merkle tree, we need the entire thing, since we never know which accounts we - // may need to access. - let snapshot = Snapshot::<_, FeeMerkleTree, { FeeMerkleTree::ARITY }>::Index(height); - let fee_merkle_tree = self - .get_snapshot(snapshot) - .await - .context("loading fee merkle tree")?; + let leaves_bytes = bincode::serialize(&leaves).context("serializing leaves")?; + let state_bytes = bincode::serialize(&state).context("serializing state")?; - Ok(ValidatedState { - block_merkle_tree, - fee_merkle_tree, + transaction(&mut self.db, |mut tx| { + async move { + tx.upsert( + "undecided_state", + ["id", "leaves", "state"], + ["id"], + [[ + sql_param(&0i32), + sql_param(&leaves_bytes), + sql_param(&state_bytes), + ]], + ) + .await?; + Ok(()) + } + .boxed() }) + .await + } + async fn append_quorum_proposal( + &mut self, + proposal: &Proposal>, + ) -> anyhow::Result<()> { + let view_number = proposal.data.view_number().u64(); + let proposal_bytes = bincode::serialize(&proposal).context("serializing proposal")?; + transaction(&mut self.db, |mut tx| { + async move { + tx.upsert( + "quorum_proposals", + ["view", "data"], + ["view"], + [[sql_param(&(view_number as i64)), sql_param(&proposal_bytes)]], + ) + .await?; + Ok(()) + } + .boxed() + }) + .await } } -fn sql_param(param: &T) -> &(dyn ToSql + Sync) { +pub(crate) fn sql_param(param: &T) -> &(dyn ToSql + Sync) { param } diff --git a/sequencer/src/reference_tests.rs b/sequencer/src/reference_tests.rs new file mode 100644 index 000000000..a76d3a7fb --- /dev/null +++ b/sequencer/src/reference_tests.rs @@ -0,0 +1,311 @@ +#![cfg(test)] +//! Reference data types. +//! +//! This module provides some reference instantiations of various data types which have an external, +//! language-independent interface (e.g. serialization or commitment scheme). Ports of the sequencer +//! to other languages, as well as downstream packages written in other languages, can use these +//! references objects and their known serializations and commitments to check that their +//! implementations are compatible with this reference implementation. +//! +//! The serialized form of each reference object, in both JSON and binary forms, is available in the +//! `data` directory of the repo for this crate. These files checked against the reference objects +//! in this module to prevent unintentional changes to the serialization format. Thus, these +//! serialized files can be used as test vectors for a port of the serialiation scheme to another +//! language or framework. +//! +//! To get the byte representation or U256 representation of a commitment for testing in other +//! packages, run the tests and look for "commitment bytes" or "commitment U256" in the logs. +//! +//! These tests may fail if you make a breaking change to a commitment scheme, serialization, etc. +//! If this happens, be sure you _want_ to break the API, and, if so, simply replace the relevant +//! constant in this module with the "actual" value that can be found in the logs of the failing +//! test. + +use crate::{ + block::NsTable, state::FeeInfo, ChainConfig, FeeAccount, Header, L1BlockInfo, Payload, + Transaction, ValidatedState, +}; +use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; +use committable::Committable; +use es_version::SequencerVersion; +use hotshot_types::traits::{ + block_contents::vid_commitment, signature_key::BuilderSignatureKey, BlockPayload, EncodeBytes, +}; +use jf_merkle_tree::MerkleTreeScheme; +use pretty_assertions::assert_eq; +use sequencer_utils::commitment_to_u256; +use serde::{de::DeserializeOwned, Serialize}; +use serde_json::Value; +use std::{fmt::Debug, path::Path, str::FromStr}; +use tagged_base64::TaggedBase64; +use vbs::BinarySerializer; + +type Serializer = vbs::Serializer; + +async fn reference_payload() -> Payload { + Payload::from_transactions( + vec![reference_transaction()], + &Default::default(), + &Default::default(), + ) + .await + .unwrap() + .0 +} + +async fn reference_ns_table() -> NsTable { + reference_payload().await.ns_table().clone() +} + +const REFERENCE_NS_TABLE_COMMITMENT: &str = "NSTABLE~jqBfNUW1lSijWpKpPNc9yxQs28YckB80gFJWnHIwOQMC"; + +fn reference_l1_block() -> L1BlockInfo { + L1BlockInfo { + number: 123, + timestamp: 0x456.into(), + hash: "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + .parse() + .unwrap(), + } +} + +const REFERENCE_L1_BLOCK_COMMITMENT: &str = "L1BLOCK~4HpzluLK2Isz3RdPNvNrDAyQcWOF2c9JeLZzVNLmfpQ9"; + +fn reference_chain_config() -> ChainConfig { + ChainConfig { + chain_id: 0x8a19.into(), + max_block_size: 10240.into(), + base_fee: 0.into(), + fee_contract: Some(Default::default()), + fee_recipient: Default::default(), + } +} + +const REFERENCE_CHAIN_CONFIG_COMMITMENT: &str = + "CHAIN_CONFIG~L6HmMktJbvnEGgpmRrsiYvQmIBstSj9UtDM7eNFFqYFO"; + +fn reference_fee_info() -> FeeInfo { + FeeInfo::new( + FeeAccount::from_str("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266").unwrap(), + 0, + ) +} + +const REFERENCE_FEE_INFO_COMMITMENT: &str = "FEE_INFO~xCCeTjJClBtwtOUrnAmT65LNTQGceuyjSJHUFfX6VRXR"; + +async fn reference_header() -> Header { + let builder_key = FeeAccount::generated_from_seed_indexed(Default::default(), 0).1; + let fee_info = reference_fee_info(); + let payload = reference_payload().await; + let ns_table = payload.ns_table().clone(); + let payload_commitment = vid_commitment(&payload.encode(), 1); + let builder_commitment = payload.builder_commitment(&ns_table); + let builder_signature = FeeAccount::sign_fee( + &builder_key, + fee_info.amount().as_u64().unwrap(), + &ns_table, + &payload_commitment, + ) + .unwrap(); + + let state = ValidatedState::default(); + + Header { + height: 42, + timestamp: 789, + l1_head: 124, + l1_finalized: Some(reference_l1_block()), + payload_commitment, + builder_commitment, + ns_table, + block_merkle_tree_root: state.block_merkle_tree.commitment(), + fee_merkle_tree_root: state.fee_merkle_tree.commitment(), + fee_info, + chain_config: reference_chain_config().into(), + builder_signature: Some(builder_signature), + } +} + +const REFERENCE_HEADER_COMMITMENT: &str = "BLOCK~mHlaknD1qKCz0UBtV2GpnqfO6gFqF5yN-9qkmLMRG3rp"; + +fn reference_transaction() -> Transaction { + let payload: [u8; 1024] = std::array::from_fn(|i| (i % (u8::MAX as usize)) as u8); + Transaction::new(12648430.into(), payload.to_vec()) +} + +const REFERENCE_TRANSACTION_COMMITMENT: &str = "TX~jmYCutMVgguprgpZHywPwkehwXfibQx951gh4LSLmfwp"; + +fn reference_test_without_committable( + name: &str, + reference: &T, +) { + setup_logging(); + setup_backtrace(); + + // Load the expected serialization from the repo. + let data_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../data"); + let expected_bytes = std::fs::read(data_dir.join(format!("{name}.json"))).unwrap(); + let expected: Value = serde_json::from_slice(&expected_bytes).unwrap(); + + // Check that the reference object matches the expected serialized form. + let actual = serde_json::to_value(reference).unwrap(); + + if actual != expected { + let actual_pretty = serde_json::to_string_pretty(&actual).unwrap(); + let expected_pretty = serde_json::to_string_pretty(&expected).unwrap(); + + // Write the actual output to a file to make it easier to compare with/replace the expected + // file if the serialization change was actually intended. + let actual_path = data_dir.join(format!("{name}-actual.json")); + std::fs::write(&actual_path, actual_pretty.as_bytes()).unwrap(); + + // Fail the test with an assertion that outputs a nice diff between the prettified JSON + // objects. + assert_eq!( + expected_pretty, + actual_pretty, + r#" +Serialized {name} does not match expected JSON. The actual serialization has been written to {}. If +you intended to make a breaking change to the API, you may replace the reference JSON file in +/data/{name}.json with /data/{name}-actual.json. Otherwise, revert your changes which have caused a +change in the serialization of this data structure. +"#, + actual_path.display() + ); + } + + // Check that we can deserialize from the reference JSON object. + let parsed: T = serde_json::from_value(expected).unwrap(); + assert_eq!( + *reference, + parsed, + "Reference object commitment does not match commitment of parsed JSON. This is indicative of + inconsistency or non-determinism in the commitment scheme.", + ); + + // Check that the reference object matches the expected binary form. + let expected = std::fs::read(data_dir.join(format!("{name}.bin"))).unwrap(); + let actual = Serializer::serialize(&reference).unwrap(); + if actual != expected { + // Write the actual output to a file to make it easier to compare with/replace the expected + // file if the serialization change was actually intended. + let actual_path = data_dir.join(format!("{name}-actual.bin")); + std::fs::write(&actual_path, &actual).unwrap(); + + // Fail the test with an assertion that outputs a diff. + // Use TaggedBase64 for compact console output. + assert_eq!( + TaggedBase64::encode_raw(&expected), + TaggedBase64::encode_raw(&actual), + r#" +Serialized {name} does not match expected binary file. The actual serialization has been written to +{}. If you intended to make a breaking change to the API, you may replace the reference file in +/data/{name}.bin with /data/{name}-actual.bin. Otherwise, revert your changes which have caused a +change in the serialization of this data structure. +"#, + actual_path.display() + ); + } + + // Check that we can deserialize from the reference binary object. + let parsed: T = Serializer::deserialize(&expected).unwrap(); + assert_eq!( + *reference, parsed, + "Reference object commitment does not match commitment of parsed binary object. This is + indicative of inconsistency or non-determinism in the commitment scheme.", + ); +} + +fn reference_test( + name: &str, + reference: T, + commitment: &str, +) { + setup_logging(); + setup_backtrace(); + + reference_test_without_committable(name, &reference); + + // Print information about the commitment that might be useful in generating tests for other + // languages. + let actual = reference.commit(); + let bytes: &[u8] = actual.as_ref(); + let u256 = commitment_to_u256(actual); + tracing::info!("actual commitment: {}", actual); + tracing::info!("commitment bytes: {:?}", bytes); + tracing::info!("commitment U256: {}", u256); + + // Check that the reference object matches the expected serialized object. + let expected = commitment.parse().unwrap(); + assert_eq!( + actual, expected, + r#" +Commitment of serialized object does not match commitment of reference object. If you intended to +make a breaking change to the API, you may replace the reference commitment constant in the +reference_tests module with the "actual" commitment below. Otherwise, revert your changes which +have caused a change to the commitment scheme. + +Expected: {expected} +Actual: {actual} +"# + ); +} + +#[async_std::test] +async fn test_reference_payload() { + reference_test_without_committable("payload", &reference_payload().await); +} + +#[async_std::test] +async fn test_reference_ns_table() { + reference_test( + "ns_table", + reference_ns_table().await, + REFERENCE_NS_TABLE_COMMITMENT, + ); +} + +#[test] +fn test_reference_l1_block() { + reference_test( + "l1_block", + reference_l1_block(), + REFERENCE_L1_BLOCK_COMMITMENT, + ); +} + +#[test] +fn test_reference_chain_config() { + reference_test( + "chain_config", + reference_chain_config(), + REFERENCE_CHAIN_CONFIG_COMMITMENT, + ); +} + +#[test] +fn test_reference_fee_info() { + reference_test( + "fee_info", + reference_fee_info(), + REFERENCE_FEE_INFO_COMMITMENT, + ); +} + +#[async_std::test] +async fn test_reference_header() { + reference_test( + "header", + reference_header().await, + REFERENCE_HEADER_COMMITMENT, + ); +} + +#[test] +fn test_reference_transaction() { + reference_test( + "transaction", + reference_transaction(), + REFERENCE_TRANSACTION_COMMITMENT, + ); +} diff --git a/sequencer/src/state.rs b/sequencer/src/state.rs index 3cd1bb98a..257e40808 100644 --- a/sequencer/src/state.rs +++ b/sequencer/src/state.rs @@ -1,69 +1,90 @@ use crate::{ - api::endpoints::AccountQueryData, catchup::StateCatchup, eth_signature_key::EthKeyPair, + api::data_source::CatchupDataSource, + block::{NsTableValidationError, PayloadByteLen}, + catchup::SqlStateCatchup, + chain_config::BlockSize, + chain_config::ResolvableChainConfig, + eth_signature_key::EthKeyPair, + genesis::UpgradeType, + persistence::ChainConfigPersistence, ChainConfig, Header, Leaf, NodeState, SeqTypes, }; -use anyhow::{anyhow, bail, ensure, Context}; +use anyhow::{bail, ensure, Context}; use ark_serialize::{ CanonicalDeserialize, CanonicalSerialize, Compress, Read, SerializationError, Valid, Validate, }; use async_std::stream::StreamExt; use async_std::sync::RwLock; -use async_trait::async_trait; use committable::{Commitment, Committable, RawCommitmentBuilder}; use contract_bindings::fee_contract::DepositFilter; use core::fmt::Debug; -use derive_more::{Add, Display, From, Into, Sub}; -use ethers::{abi::Address, types::U256}; +use derive_more::{Add, Display, From, Into, Mul, Sub}; +use ethers::{ + abi::Address, + types::U256, + utils::{parse_units, ParseUnits}, +}; use futures::future::Future; use hotshot::traits::ValidatedState as HotShotState; use hotshot_query_service::{ availability::{AvailabilityDataSource, LeafQueryData}, data_source::VersionedDataSource, - merklized_state::{ - MerklizedState, MerklizedStateDataSource, MerklizedStateHeightPersistence, Snapshot, - UpdateStateData, - }, + explorer::MonetaryValue, + merklized_state::{MerklizedState, MerklizedStateHeightPersistence, UpdateStateData}, types::HeightIndexed, }; use hotshot_types::{ data::{BlockError, ViewNumber}, traits::{ - node_implementation::ConsensusTime, signature_key::BuilderSignatureKey, states::StateDelta, + block_contents::{BlockHeader, BuilderFee}, + node_implementation::ConsensusTime, + signature_key::BuilderSignatureKey, + states::StateDelta, }, + vid::{VidCommon, VidSchemeType}, }; use itertools::Itertools; -use jf_primitives::merkle_tree::{ - prelude::{MerkleNode, MerkleProof}, - ToTraversalPath, UniversalMerkleTreeScheme, -}; -use jf_primitives::{ - errors::PrimitivesError, - merkle_tree::{ - prelude::{LightWeightSHA3MerkleTree, Sha3Digest, Sha3Node}, - universal_merkle_tree::UniversalMerkleTree, - AppendableMerkleTreeScheme, ForgetableMerkleTreeScheme, - ForgetableUniversalMerkleTreeScheme, LookupResult, MerkleCommitment, MerkleTreeScheme, - }, +use jf_merkle_tree::{ + prelude::{LightWeightSHA3MerkleTree, MerkleProof, Sha3Digest, Sha3Node}, + universal_merkle_tree::UniversalMerkleTree, + AppendableMerkleTreeScheme, ForgetableMerkleTreeScheme, ForgetableUniversalMerkleTreeScheme, + LookupResult, MerkleCommitment, MerkleTreeError, MerkleTreeScheme, + PersistentUniversalMerkleTreeScheme, ToTraversalPath, UniversalMerkleTreeScheme, }; +use jf_vid::VidScheme; use num_traits::CheckedSub; -use sequencer_utils::impl_to_fixed_bytes; +use sequencer_utils::{ + impl_serde_from_string_or_integer, impl_to_fixed_bytes, ser::FromStringOrInteger, +}; use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::time::Duration; use std::{collections::HashSet, ops::Add, str::FromStr}; +use thiserror::Error; +use vbs::version::Version; const BLOCK_MERKLE_TREE_HEIGHT: usize = 32; const FEE_MERKLE_TREE_HEIGHT: usize = 20; +/// This enum is not used in code but functions as an index of +/// possible validation errors. +#[allow(dead_code)] +enum StateValidationError { + ProposalValidation(ProposalValidationError), + BuilderValidation(BuilderValidationError), + Fee(FeeError), +} + #[derive(Hash, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct ValidatedState { /// Frontier of Block Merkle Tree pub block_merkle_tree: BlockMerkleTree, /// Fee Merkle Tree pub fee_merkle_tree: FeeMerkleTree, + pub chain_config: ResolvableChainConfig, } -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] pub struct Delta { pub fees_delta: HashSet, } @@ -86,9 +107,13 @@ impl Default for ValidatedState { Vec::<(FeeAccount, FeeAmount)>::new(), ) .unwrap(); + + let chain_config = ResolvableChainConfig::from(ChainConfig::default()); + Self { block_merkle_tree, fee_merkle_tree, + chain_config, } } } @@ -99,6 +124,14 @@ impl ValidatedState { self.fee_merkle_tree.update(account, amount).unwrap(); } + pub fn balance(&mut self, account: FeeAccount) -> Option { + match self.fee_merkle_tree.lookup(account) { + LookupResult::Ok(balance, _) => Some(*balance), + LookupResult::NotFound(_) => Some(0.into()), + LookupResult::NotInMemory => None, + } + } + /// Find accounts that are not in memory. /// /// As an optimization we could try to apply updates and return the @@ -136,24 +169,26 @@ impl ValidatedState { pub fn insert_fee_deposit( &mut self, fee_info: FeeInfo, - ) -> Result, PrimitivesError> { - self.fee_merkle_tree + ) -> anyhow::Result> { + Ok(self + .fee_merkle_tree .update_with(fee_info.account, |balance| { Some(balance.cloned().unwrap_or_default().add(fee_info.amount)) - }) + })?) } - /// Charge a fee to an account. - pub fn charge_fee(&mut self, fee_info: FeeInfo) -> anyhow::Result<()> { + /// Charge a fee to an account, transferring the funds to the fee recipient account. + pub fn charge_fee(&mut self, fee_info: FeeInfo, recipient: FeeAccount) -> Result<(), FeeError> { + let fee_state = self.fee_merkle_tree.clone(); + + // Deduct the fee from the paying account. let FeeInfo { account, amount } = fee_info; let mut err = None; - let res = self.fee_merkle_tree.update_with(account, |balance| { + let fee_state = fee_state.persistent_update_with(account, |balance| { let balance = balance.copied(); let Some(updated) = balance.unwrap_or_default().checked_sub(&amount) else { // Return an error without updating the account. - err = Some(anyhow!( - "insufficient funds (have {balance:?}, required {amount:?})" - )); + err = Some(FeeError::InsufficientFunds { balance, amount }); return balance; }; if updated == FeeAmount::default() { @@ -165,17 +200,21 @@ impl ValidatedState { Some(updated) } })?; - // Check if we were unable to do the update because the required Merkle path is missing. - ensure!( - res.expect_not_in_memory().is_err(), - format!("missing account state for {account}") - ); - // Fail if there was an error during `update_with`, otherwise succeed. + + // Fail if there was an error during `persistent_update_with` (e.g. insufficient balance). if let Some(err) = err { - Err(err) - } else { - Ok(()) + return Err(err); } + + // If we successfully deducted the fee from the source account, increment the balance of the + // recipient account. + let fee_state = fee_state.persistent_update_with(recipient, |balance| { + Some(balance.copied().unwrap_or_default() + amount) + })?; + + // If the whole update was successful, update the original state. + self.fee_merkle_tree = fee_state; + Ok(()) } } @@ -187,14 +226,53 @@ impl ValidatedState { block_merkle_tree: BlockMerkleTree::from_commitment( self.block_merkle_tree.commitment(), ), + chain_config: ResolvableChainConfig::from(self.chain_config.commit()), } } } -// TODO remove this, data will come from HotShot: -// https://github.com/EspressoSystems/HotShot/issues/2744 -fn get_proposed_payload_size() -> u64 { - 1 +/// Possible proposal validation failures +#[derive(Error, Debug, Eq, PartialEq)] +pub enum ProposalValidationError { + #[error("Invalid ChainConfig: expected={expected}, proposal={proposal}")] + InvalidChainConfig { expected: String, proposal: String }, + + #[error( + "Invalid Payload Size: (max_block_size={max_block_size}, proposed_block_size={block_size})" + )] + MaxBlockSizeExceeded { + max_block_size: BlockSize, + block_size: BlockSize, + }, + #[error("Insufficient Fee: block_size={max_block_size}, base_fee={base_fee}, proposed_fee={proposed_fee}")] + InsufficientFee { + max_block_size: BlockSize, + base_fee: FeeAmount, + proposed_fee: FeeAmount, + }, + #[error("Invalid Height: parent_height={parent_height}, proposal_height={proposal_height}")] + InvalidHeight { + parent_height: u64, + proposal_height: u64, + }, + #[error("Invalid Block Root Error: expected={expected_root}, proposal={proposal_root}")] + InvalidBlockRoot { + expected_root: BlockMerkleCommitment, + proposal_root: BlockMerkleCommitment, + }, + #[error("Invalid Fee Root Error: expected={expected_root}, proposal={proposal_root}")] + InvalidFeeRoot { + expected_root: FeeMerkleCommitment, + proposal_root: FeeMerkleCommitment, + }, + #[error("Invalid namespace table: {err}")] + InvalidNsTable { err: NsTableValidationError }, +} + +impl From for ProposalValidationError { + fn from(err: NsTableValidationError) -> Self { + Self::InvalidNsTable { err } + } } pub fn validate_proposal( @@ -202,224 +280,176 @@ pub fn validate_proposal( expected_chain_config: ChainConfig, parent_leaf: &Leaf, proposal: &Header, -) -> anyhow::Result<()> { - let parent_header = parent_leaf.get_block_header(); + vid_common: &VidCommon, +) -> Result<(), ProposalValidationError> { + let parent_header = parent_leaf.block_header(); // validate `ChainConfig` - anyhow::ensure!( - proposal.chain_config.commit() == expected_chain_config.commit(), - anyhow::anyhow!( - "Invalid Chain Config: local={:?}, proposal={:?}", - expected_chain_config, - proposal.chain_config - ) - ); + if proposal.chain_config.commit() != expected_chain_config.commit() { + return Err(ProposalValidationError::InvalidChainConfig { + expected: format!("{:?}", expected_chain_config), + proposal: format!("{:?}", proposal.chain_config), + }); + } - anyhow::ensure!( - get_proposed_payload_size() < expected_chain_config.max_block_size(), - anyhow::anyhow!( - "Invalid Payload Size: local={:?}, proposal={:?}", - expected_chain_config, - proposal.chain_config - ) - ); + // validate block size and fee + let block_size = VidSchemeType::get_payload_byte_len(vid_common) as u64; + if block_size > *expected_chain_config.max_block_size { + return Err(ProposalValidationError::MaxBlockSizeExceeded { + max_block_size: expected_chain_config.max_block_size, + block_size: block_size.into(), + }); + } + + if proposal.fee_info.amount() < expected_chain_config.base_fee * block_size { + return Err(ProposalValidationError::InsufficientFee { + max_block_size: expected_chain_config.max_block_size, + base_fee: expected_chain_config.base_fee, + proposed_fee: proposal.fee_info.amount(), + }); + } // validate height - anyhow::ensure!( - proposal.height == parent_header.height + 1, - anyhow::anyhow!( - "Invalid Height Error: {}, {}", - parent_header.height, - proposal.height - ) - ); + if proposal.height != parent_header.height + 1 { + return Err(ProposalValidationError::InvalidHeight { + parent_height: parent_header.height, + proposal_height: proposal.height, + }); + } let ValidatedState { block_merkle_tree, fee_merkle_tree, + .. } = state; let block_merkle_tree_root = block_merkle_tree.commitment(); - anyhow::ensure!( - proposal.block_merkle_tree_root == block_merkle_tree_root, - anyhow::anyhow!( - "Invalid Block Root Error: local={}, proposal={}", - block_merkle_tree_root, - proposal.block_merkle_tree_root - ) - ); + if proposal.block_merkle_tree_root != block_merkle_tree_root { + return Err(ProposalValidationError::InvalidBlockRoot { + expected_root: block_merkle_tree_root, + proposal_root: proposal.block_merkle_tree_root, + }); + } let fee_merkle_tree_root = fee_merkle_tree.commitment(); - anyhow::ensure!( - proposal.fee_merkle_tree_root == fee_merkle_tree_root, - anyhow::anyhow!( - "Invalid Fee Root Error: local={}, proposal={}", - fee_merkle_tree_root, - proposal.fee_merkle_tree_root - ) - ); + if proposal.fee_merkle_tree_root != fee_merkle_tree_root { + return Err(ProposalValidationError::InvalidFeeRoot { + expected_root: fee_merkle_tree_root, + proposal_root: proposal.fee_merkle_tree_root, + }); + } + + proposal + .ns_table + .validate(&PayloadByteLen::from_vid_common(vid_common))?; + Ok(()) } +/// Possible charge fee failures +#[derive(Error, Debug, Eq, PartialEq)] +pub enum FeeError { + #[error("Insuficcient Funds: have {balance:?}, required {amount:?}")] + InsufficientFunds { + balance: Option, + amount: FeeAmount, + }, + #[error("Merkle Tree Error: {0}")] + MerkleTreeError(MerkleTreeError), +} + +impl From for FeeError { + fn from(item: MerkleTreeError) -> Self { + Self::MerkleTreeError(item) + } +} + fn charge_fee( state: &mut ValidatedState, delta: &mut Delta, fee_info: FeeInfo, -) -> anyhow::Result<()> { - state.charge_fee(fee_info)?; - delta.fees_delta.insert(fee_info.account); + recipient: FeeAccount, +) -> Result<(), FeeError> { + state.charge_fee(fee_info, recipient)?; + delta.fees_delta.extend([fee_info.account, recipient]); Ok(()) } +/// Possible builder validation failures +#[derive(Error, Debug, Eq, PartialEq)] +pub enum BuilderValidationError { + #[error("Builder signature not found")] + SignatureNotFound, + #[error("Fee amount out of range: {0}")] + FeeAmountOutOfRange(FeeAmount), + #[error("Invalid Builder Signature")] + InvalidBuilderSignature, +} + /// Validate builder account by verifying signature -fn validate_builder_fee(proposed_header: &Header) -> anyhow::Result<()> { +fn validate_builder_fee(proposed_header: &Header) -> Result<(), BuilderValidationError> { // Beware of Malice! let signature = proposed_header .builder_signature - .ok_or_else(|| anyhow::anyhow!("Builder signature not found"))?; - let msg = proposed_header.fee_message().context("invalid fee")?; - // verify signature - anyhow::ensure!( - proposed_header - .fee_info - .account - .validate_builder_signature(&signature, msg.as_ref()), - "Invalid Builder Signature" - ); - - Ok(()) -} - -#[derive(Debug)] -struct SqlStateCatchup { - db: Arc>, - block_height: u64, -} - -#[async_trait] -impl StateCatchup for SqlStateCatchup -where - T: SequencerStateDataSource, -{ - async fn fetch_accounts( - &self, - _view: ViewNumber, - _fee_merkle_tree_root: FeeMerkleCommitment, - accounts: Vec, - ) -> anyhow::Result> { - let mut ret = vec![]; - for account in accounts { - let block_height = self.block_height; - - let proof = self - .db - .read() - .await - .get_path( - Snapshot::::Index( - block_height, - ), - account, - ) - .await - .context(format!("fetching account {account}; height {block_height}"))?; - - match proof.proof.first().context(format!( - "empty proof for account {account}; height {block_height}" - ))? { - MerkleNode::Leaf { pos, elem, .. } => { - ret.push(AccountQueryData { - balance: elem.0, - proof: FeeAccountProof { - account: (*pos).into(), - proof: FeeMerkleProof::Presence(proof), - }, - }); - } + .ok_or(BuilderValidationError::SignatureNotFound)?; + let fee_amount = proposed_header.fee_info.amount().as_u64().ok_or( + BuilderValidationError::FeeAmountOutOfRange(proposed_header.fee_info.amount()), + )?; - MerkleNode::Empty => { - ret.push(AccountQueryData { - balance: 0_u64.into(), - proof: FeeAccountProof { - account: account.into(), - proof: FeeMerkleProof::Absence(proof), - }, - }); - } - _ => { - bail!("Invalid proof"); - } - } - } - Ok(ret) + // verify signature + if !proposed_header.fee_info.account.validate_fee_signature( + &signature, + fee_amount, + proposed_header.metadata(), + &proposed_header.payload_commitment(), + ) { + return Err(BuilderValidationError::InvalidBuilderSignature); } - async fn remember_blocks_merkle_tree( - &self, - _view: ViewNumber, - mt: &mut BlockMerkleTree, - ) -> anyhow::Result<()> { - let bh = self.block_height; - - if bh == 0 { - return Ok(()); - } - - let proof = self - .db - .read() - .await - .get_path( - Snapshot::::Index(bh), - bh - 1, - ) - .await - .context(format!("fetching frontier at height {bh}"))?; - - match proof - .proof - .first() - .context(format!("empty proof for frontier at height {bh}"))? - { - MerkleNode::Leaf { pos, elem, .. } => mt - .remember(pos, elem, proof.clone()) - .context("failed to remember proof"), - _ => bail!("invalid proof"), - } - } + Ok(()) } async fn compute_state_update( - storage: Arc>, - instance: &mut NodeState, + state: &ValidatedState, + instance: &NodeState, parent_leaf: &LeafQueryData, proposed_leaf: &LeafQueryData, + version: Version, ) -> anyhow::Result<(ValidatedState, Delta)> { let proposed_leaf = proposed_leaf.leaf(); let parent_leaf = parent_leaf.leaf(); - let header = proposed_leaf.get_block_header(); - let validated_state = ValidatedState::from_header(parent_leaf.get_block_header()); + let header = proposed_leaf.block_header(); - let catchup = SqlStateCatchup { - db: storage, - block_height: parent_leaf.get_height(), - }; - instance.peers = Arc::new(catchup); + // Check internal consistency. + let parent_header = parent_leaf.block_header(); + ensure!( + state.block_merkle_tree.commitment() == parent_header.block_merkle_tree_root, + "internal error! in-memory block tree {:?} does not match parent header {:?}", + state.block_merkle_tree.commitment(), + parent_header.block_merkle_tree_root + ); + ensure!( + state.fee_merkle_tree.commitment() == parent_header.fee_merkle_tree_root, + "internal error! in-memory fee tree {:?} does not match parent header {:?}", + state.fee_merkle_tree.commitment(), + parent_header.fee_merkle_tree_root + ); - validated_state - .apply_header(instance, parent_leaf, header) + state + .apply_header(instance, parent_leaf, header, version) .await } async fn store_state_update( storage: &mut impl SequencerStateDataSource, block_number: u64, - state: ValidatedState, + state: &ValidatedState, delta: Delta, ) -> anyhow::Result<()> { let ValidatedState { fee_merkle_tree, block_merkle_tree, + .. } = state; let Delta { fees_delta } = delta; @@ -475,29 +505,51 @@ async fn store_state_update( Ok(()) } +#[tracing::instrument( + skip_all, + fields( + node_id = instance.node_id, + view = ?parent_leaf.leaf().view_number(), + height = parent_leaf.height(), + ), +)] async fn update_state_storage( + parent_state: &ValidatedState, storage: &Arc>, - instance: &mut NodeState, + instance: &NodeState, parent_leaf: &LeafQueryData, proposed_leaf: &LeafQueryData, -) -> anyhow::Result<()> { + version: Version, +) -> anyhow::Result { + let parent_chain_config = parent_state.chain_config; + let (state, delta) = - compute_state_update(storage.clone(), instance, parent_leaf, proposed_leaf) + compute_state_update(parent_state, instance, parent_leaf, proposed_leaf, version) .await .context("computing state update")?; let mut storage = storage.write().await; - if let Err(err) = store_state_update(&mut *storage, proposed_leaf.height(), state, delta).await + if let Err(err) = store_state_update(&mut *storage, proposed_leaf.height(), &state, delta).await { storage.revert().await; return Err(err); } - Ok(()) + if parent_chain_config != state.chain_config { + let cf = state + .chain_config + .resolve() + .context("failed to resolve to chain config")?; + + storage.insert_chain_config(cf).await? + } + + Ok(state) } async fn store_genesis_state( storage: &mut impl SequencerStateDataSource, + chain_config: ChainConfig, state: &ValidatedState, ) -> anyhow::Result<()> { ensure!( @@ -525,15 +577,19 @@ async fn store_genesis_state( .context("failed to store fee merkle nodes")?; } + storage.insert_chain_config(chain_config).await?; + storage.commit().await?; Ok(()) } -pub async fn update_state_storage_loop( +pub(crate) async fn update_state_storage_loop( storage: Arc>, instance: impl Future, + version: Version, ) -> anyhow::Result<()> { let mut instance = instance.await; + instance.peers = Arc::new(SqlStateCatchup::new(storage.clone(), Default::default())); // get last saved merklized state let (last_height, parent_leaf, mut leaves) = { @@ -549,13 +605,20 @@ pub async fn update_state_storage_loop( // resolve the parent leaf future _after_ dropping our lock on the state, in case it is not // ready yet and another task needs a mutable lock on the state to produce the parent leaf. let mut parent_leaf = parent_leaf.await; + let mut parent_state = ValidatedState::from_header(parent_leaf.header()); if last_height == 0 { // If the last height is 0, we need to insert the genesis state, since this state is // never the result of a state update and thus is not inserted in the loop below. tracing::info!("storing genesis merklized state"); let mut storage = storage.write().await; - if let Err(err) = store_genesis_state(&mut *storage, &instance.genesis_state).await { + if let Err(err) = store_genesis_state( + &mut *storage, + instance.chain_config, + &instance.genesis_state, + ) + .await + { tracing::error!("failed to store genesis state: {err:#}"); storage.revert().await; return Err(err); @@ -564,9 +627,19 @@ pub async fn update_state_storage_loop( while let Some(leaf) = leaves.next().await { loop { - match update_state_storage(&storage, &mut instance, &parent_leaf, &leaf).await { - Ok(()) => { + match update_state_storage( + &parent_state, + &storage, + &instance, + &parent_leaf, + &leaf, + version, + ) + .await + { + Ok(state) => { parent_leaf = leaf; + parent_state = state; break; } Err(err) => { @@ -581,16 +654,16 @@ pub async fn update_state_storage_loop( Ok(()) } -pub trait SequencerStateDataSource: +pub(crate) trait SequencerStateDataSource: 'static + Debug + AvailabilityDataSource + VersionedDataSource - + MerklizedStateDataSource - + MerklizedStateDataSource + + CatchupDataSource + UpdateStateData + UpdateStateData + MerklizedStateHeightPersistence + + ChainConfigPersistence { } @@ -599,57 +672,89 @@ impl SequencerStateDataSource for T where + Debug + AvailabilityDataSource + VersionedDataSource - + MerklizedStateDataSource - + MerklizedStateDataSource + + CatchupDataSource + UpdateStateData + UpdateStateData + MerklizedStateHeightPersistence + + ChainConfigPersistence { } impl ValidatedState { - async fn apply_header( + pub(crate) async fn apply_header( &self, instance: &NodeState, parent_leaf: &Leaf, proposed_header: &Header, + version: Version, ) -> anyhow::Result<(Self, Delta)> { // Clone state to avoid mutation. Consumer can take update // through returned value. - let l1_deposits = get_l1_deposits(instance, proposed_header, parent_leaf).await; - let mut validated_state = self.clone(); + validated_state.apply_upgrade(instance, version); - let accounts = std::iter::once(proposed_header.fee_info.account); + let chain_config = validated_state + .get_chain_config(instance, &proposed_header.chain_config) + .await?; - // Find missing state entries + if Some(chain_config) != validated_state.chain_config.resolve() { + validated_state.chain_config = chain_config.into(); + } + + let l1_deposits = get_l1_deposits( + instance, + proposed_header, + parent_leaf, + chain_config.fee_contract, + ) + .await; + + // Find missing fee state entries. We will need to use the builder account which is paying a + // fee and the recipient account which is receiving it, plus any counts receiving deposits + // in this block. let missing_accounts = self.forgotten_accounts( - accounts.chain(l1_deposits.iter().map(|fee_info| fee_info.account)), + [proposed_header.fee_info.account, chain_config.fee_recipient] + .into_iter() + .chain(l1_deposits.iter().map(|fee_info| fee_info.account)), ); - let view = parent_leaf.get_view_number(); + let parent_height = parent_leaf.height(); + let parent_view = parent_leaf.view_number(); // Ensure merkle tree has frontier if self.need_to_fetch_blocks_mt_frontier() { - tracing::info!("fetching block frontier for view {view:?} from peers"); - + tracing::info!( + parent_height, + ?parent_view, + "fetching block frontier from peers" + ); instance .peers .as_ref() - .remember_blocks_merkle_tree(view, &mut validated_state.block_merkle_tree) + .remember_blocks_merkle_tree( + parent_height, + parent_view, + &mut validated_state.block_merkle_tree, + ) .await?; } // Fetch missing fee state entries if !missing_accounts.is_empty() { - tracing::info!("fetching missing accounts {missing_accounts:?} from peers"); + tracing::info!( + parent_height, + ?parent_view, + ?missing_accounts, + "fetching missing accounts from peers" + ); let missing_account_proofs = instance .peers .as_ref() .fetch_accounts( - view, + parent_height, + parent_view, validated_state.fee_merkle_tree.commitment(), missing_accounts, ) @@ -669,23 +774,81 @@ impl ValidatedState { let mut validated_state = apply_proposal(&validated_state, &mut delta, parent_leaf, l1_deposits); - charge_fee(&mut validated_state, &mut delta, proposed_header.fee_info)?; + charge_fee( + &mut validated_state, + &mut delta, + proposed_header.fee_info, + chain_config.fee_recipient, + )?; Ok((validated_state, delta)) } + + /// Updates the `ValidatedState` if a protocol upgrade has occurred. + pub(crate) fn apply_upgrade(&mut self, instance: &NodeState, version: Version) { + // Check for protocol upgrade based on sequencer version + if version <= instance.current_version { + return; + } + + let Some(upgrade) = instance.upgrades.get(&version) else { + return; + }; + + match upgrade.upgrade_type { + UpgradeType::ChainConfig { chain_config } => { + self.chain_config = chain_config.into(); + } + } + } + + /// Retrieves the `ChainConfig`. + /// + /// Returns the `NodeState` `ChainConfig` if the `ValidatedState` `ChainConfig` commitment matches the `NodeState` `ChainConfig`` commitment. + /// If the commitments do not match, it returns the `ChainConfig` available in either `ValidatedState` or proposed header. + /// If neither has the `ChainConfig`, it fetches the config from the peers. + /// + /// Returns an error if it fails to fetch the `ChainConfig` from the peers. + pub(crate) async fn get_chain_config( + &self, + instance: &NodeState, + header_cf: &ResolvableChainConfig, + ) -> anyhow::Result { + let state_cf = self.chain_config; + + if state_cf.commit() == instance.chain_config.commit() { + return Ok(instance.chain_config); + } + + let cf = match (state_cf.resolve(), header_cf.resolve()) { + (Some(cf), _) => cf, + (_, Some(cf)) if cf.commit() == state_cf.commit() => cf, + (_, Some(_)) | (None, None) => { + instance + .peers + .as_ref() + .fetch_chain_config(state_cf.commit()) + .await + } + }; + + Ok(cf) + } } pub async fn get_l1_deposits( instance: &NodeState, header: &Header, parent_leaf: &Leaf, + fee_contract_address: Option
, ) -> Vec { - if let Some(block_info) = header.l1_finalized { + if let (Some(addr), Some(block_info)) = (fee_contract_address, header.l1_finalized) { instance .l1_client .get_finalized_deposits( + addr, parent_leaf - .get_block_header() + .block_header() .l1_finalized .map(|block_info| block_info.number), block_info.number, @@ -697,7 +860,7 @@ pub async fn get_l1_deposits( } #[must_use] -pub fn apply_proposal( +fn apply_proposal( validated_state: &ValidatedState, delta: &mut Delta, parent_leaf: &Leaf, @@ -707,7 +870,7 @@ pub fn apply_proposal( // pushing a block into merkle tree shouldn't fail validated_state .block_merkle_tree - .push(parent_leaf.get_block_header().commit()) + .push(parent_leaf.block_header().commit()) .unwrap(); for FeeInfo { account, amount } in l1_deposits.iter() { @@ -735,13 +898,19 @@ impl HotShotState for ValidatedState { /// proposal descends from parent. Returns updated `ValidatedState`. #[tracing::instrument( skip_all, - fields(view = ?parent_leaf.get_view_number(), height = parent_leaf.get_block_header().height), + fields( + node_id = instance.node_id, + view = ?parent_leaf.view_number(), + height = parent_leaf.height(), + ), )] async fn validate_and_apply_header( &self, instance: &Self::Instance, parent_leaf: &Leaf, proposed_header: &Header, + vid_common: VidCommon, + version: Version, ) -> Result<(Self, Self::Delta), Self::Error> { //validate builder fee if let Err(err) = validate_builder_fee(proposed_header) { @@ -752,16 +921,22 @@ impl HotShotState for ValidatedState { // Unwrapping here is okay as we retry in a loop //so we should either get a validated state or until hotshot cancels the task let (validated_state, delta) = self - .apply_header(instance, parent_leaf, proposed_header) + .apply_header(instance, parent_leaf, proposed_header, version) .await .unwrap(); + let chain_config = validated_state + .chain_config + .resolve() + .expect("Chain Config not found in validated state"); + // validate the proposal if let Err(err) = validate_proposal( &validated_state, - instance.chain_config, + chain_config, parent_leaf, proposed_header, + &vid_common, ) { tracing::error!("invalid proposal: {err:#}"); return Err(BlockError::InvalidBlockHeader); @@ -769,7 +944,7 @@ impl HotShotState for ValidatedState { // log successful progress about once in 10 - 20 seconds, // TODO: we may want to make this configurable - if parent_leaf.get_view_number().get_u64() % 10 == 0 { + if parent_leaf.view_number().u64() % 10 == 0 { tracing::info!("validated and applied new header"); } Ok((validated_state, delta)) @@ -795,6 +970,7 @@ impl HotShotState for ValidatedState { Self { fee_merkle_tree, block_merkle_tree, + chain_config: block_header.chain_config, } } /// Construct a genesis validated state. @@ -908,6 +1084,15 @@ impl FeeInfo { } } +impl From> for FeeInfo { + fn from(fee: BuilderFee) -> Self { + Self { + amount: fee.fee_amount.into(), + account: fee.fee_account, + } + } +} + impl From for FeeInfo { fn from(item: DepositFilter) -> Self { Self { @@ -937,19 +1122,21 @@ impl Committable for FeeInfo { Copy, Clone, Debug, - Deserialize, - Serialize, + Display, PartialEq, Eq, PartialOrd, Ord, Add, Sub, + Mul, From, Into, )] +#[display(fmt = "{_0}")] pub struct FeeAmount(U256); +impl_serde_from_string_or_integer!(FeeAmount); impl_to_fixed_bytes!(FeeAmount, U256); impl From for FeeAmount { @@ -958,14 +1145,66 @@ impl From for FeeAmount { } } +impl From for MonetaryValue { + fn from(value: FeeAmount) -> Self { + MonetaryValue::eth(value.0.as_u128() as i128) + } +} + impl CheckedSub for FeeAmount { fn checked_sub(&self, v: &Self) -> Option { self.0.checked_sub(v.0).map(FeeAmount) } } +impl FromStr for FeeAmount { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + Ok(Self(s.parse()?)) + } +} + +impl FromStringOrInteger for FeeAmount { + type Binary = U256; + type Integer = u64; + + fn from_binary(b: Self::Binary) -> anyhow::Result { + Ok(Self(b)) + } + + fn from_integer(i: Self::Integer) -> anyhow::Result { + Ok(i.into()) + } + + fn from_string(s: String) -> anyhow::Result { + // For backwards compatibility, we have an ad hoc parser for WEI amounts represented as hex + // strings. + if let Some(s) = s.strip_prefix("0x") { + return Ok(Self(s.parse()?)); + } + + // Strip an optional non-numeric suffix, which will be interpreted as a unit. + let (base, unit) = s + .split_once(char::is_whitespace) + .unwrap_or((s.as_str(), "wei")); + match parse_units(base, unit)? { + ParseUnits::U256(n) => Ok(Self(n)), + ParseUnits::I256(_) => bail!("amount cannot be negative"), + } + } + + fn to_binary(&self) -> anyhow::Result { + Ok(self.0) + } + + fn to_string(&self) -> anyhow::Result { + Ok(format!("{self}")) + } +} + impl FeeAmount { - pub(crate) fn as_u64(&self) -> Option { + pub fn as_u64(&self) -> Option { if self.0 <= u64::MAX.into() { Some(self.0.as_u64()) } else { @@ -1150,6 +1389,26 @@ enum FeeMerkleProof { } impl FeeAccountProof { + pub(crate) fn presence( + pos: FeeAccount, + proof: ::MembershipProof, + ) -> Self { + Self { + account: pos.into(), + proof: FeeMerkleProof::Presence(proof), + } + } + + pub(crate) fn absence( + pos: FeeAccount, + proof: ::NonMembershipProof, + ) -> Self { + Self { + account: pos.into(), + proof: FeeMerkleProof::Absence(proof), + } + } + pub fn prove(tree: &FeeMerkleTree, account: Address) -> Option<(Self, U256)> { match tree.universal_lookup(FeeAccount(account)) { LookupResult::Ok(balance, proof) => Some(( @@ -1217,6 +1476,8 @@ impl FeeAccountProof { mod test { use super::*; use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; + use hotshot_types::vid::vid_scheme; + use jf_vid::VidScheme; #[test] fn test_fee_proofs() { @@ -1256,4 +1517,176 @@ mod test { FeeAccountProof::prove(&tree, account1).unwrap(); FeeAccountProof::prove(&tree, account2).unwrap(); } + + #[async_std::test] + async fn test_validation_max_block_size() { + setup_logging(); + setup_backtrace(); + + const MAX_BLOCK_SIZE: usize = 10; + let payload = [0; 2 * MAX_BLOCK_SIZE]; + let vid_common = vid_scheme(1).disperse(payload).unwrap().common; + + let state = ValidatedState::default(); + let instance = NodeState::mock().with_chain_config(ChainConfig { + max_block_size: (MAX_BLOCK_SIZE as u64).into(), + base_fee: 0.into(), + ..Default::default() + }); + let parent = Leaf::genesis(&instance.genesis_state, &instance).await; + let header = parent.block_header(); + + // Validation fails because the proposed block exceeds the maximum block size. + let err = validate_proposal(&state, instance.chain_config, &parent, header, &vid_common) + .unwrap_err(); + + tracing::info!(%err, "task failed successfully"); + assert_eq!( + ProposalValidationError::MaxBlockSizeExceeded { + max_block_size: instance.chain_config.max_block_size, + block_size: BlockSize::from_integer( + VidSchemeType::get_payload_byte_len(&vid_common).into() + ) + .unwrap() + }, + err + ); + } + + #[async_std::test] + async fn test_validation_base_fee() { + setup_logging(); + setup_backtrace(); + + let max_block_size = 10; + let payload = [0; 1]; + let vid_common = vid_scheme(1).disperse(payload).unwrap().common; + + let state = ValidatedState::default(); + let instance = NodeState::mock().with_chain_config(ChainConfig { + base_fee: 1000.into(), // High base fee + max_block_size: max_block_size.into(), + ..Default::default() + }); + let parent = Leaf::genesis(&instance.genesis_state, &instance).await; + let header = parent.block_header(); + + // Validation fails because the genesis fee (0) is too low. + let err = validate_proposal(&state, instance.chain_config, &parent, header, &vid_common) + .unwrap_err(); + + tracing::info!(%err, "task failed successfully"); + assert_eq!( + ProposalValidationError::InsufficientFee { + max_block_size: instance.chain_config.max_block_size, + base_fee: instance.chain_config.base_fee, + proposed_fee: header.fee_info.amount() + }, + err + ); + } + + #[test] + fn test_charge_fee() { + setup_logging(); + setup_backtrace(); + + let src = FeeAccount::generated_from_seed_indexed([0; 32], 0).0; + let dst = FeeAccount::generated_from_seed_indexed([0; 32], 1).0; + let amt = FeeAmount::from(1); + + let fee_info = FeeInfo::new(src, amt); + + let new_state = || { + let mut state = ValidatedState::default(); + state.prefund_account(src, amt); + state + }; + + tracing::info!("test successful fee"); + let mut state = new_state(); + state.charge_fee(fee_info, dst).unwrap(); + assert_eq!(state.balance(src), Some(0.into())); + assert_eq!(state.balance(dst), Some(amt)); + + tracing::info!("test insufficient balance"); + let err = state.charge_fee(fee_info, dst).unwrap_err(); + assert_eq!(state.balance(src), Some(0.into())); + assert_eq!(state.balance(dst), Some(amt)); + assert_eq!( + FeeError::InsufficientFunds { + balance: None, + amount: amt + }, + err + ); + + tracing::info!("test src not in memory"); + let mut state = new_state(); + state.fee_merkle_tree.forget(src).expect_ok().unwrap(); + assert_eq!( + FeeError::MerkleTreeError(MerkleTreeError::ForgottenLeaf), + state.charge_fee(fee_info, dst).unwrap_err() + ); + + tracing::info!("test dst not in memory"); + let mut state = new_state(); + state.prefund_account(dst, amt); + state.fee_merkle_tree.forget(dst).expect_ok().unwrap(); + assert_eq!( + FeeError::MerkleTreeError(MerkleTreeError::ForgottenLeaf), + state.charge_fee(fee_info, dst).unwrap_err() + ); + } + + #[test] + fn test_fee_amount_serde_json_as_decimal() { + let amt = FeeAmount::from(123); + let serialized = serde_json::to_string(&amt).unwrap(); + + // The value is serialized as a decimal string. + assert_eq!(serialized, "\"123\""); + + // Deserialization produces the original value + let deserialized: FeeAmount = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, amt); + } + + #[test] + fn test_fee_amount_from_units() { + for (unit, multiplier) in [ + ("wei", 1), + ("gwei", 1_000_000_000), + ("eth", 1_000_000_000_000_000_000), + ] { + let amt: FeeAmount = serde_json::from_str(&format!("\"1 {unit}\"")).unwrap(); + assert_eq!(amt, multiplier.into()); + } + } + + #[test] + fn test_fee_amount_serde_json_from_hex() { + // For backwards compatibility, fee amounts can also be deserialized from a 0x-prefixed hex + // string. + let amt: FeeAmount = serde_json::from_str("\"0x123\"").unwrap(); + assert_eq!(amt, FeeAmount::from(0x123)); + } + + #[test] + fn test_fee_amount_serde_json_from_number() { + // For convenience, fee amounts can also be deserialized from a JSON number. + let amt: FeeAmount = serde_json::from_str("123").unwrap(); + assert_eq!(amt, FeeAmount::from(123)); + } + + #[test] + fn test_fee_amount_serde_bincode_unchanged() { + // For non-human-readable formats, FeeAmount just serializes as the underlying U256. + let n = U256::from(123); + let amt = FeeAmount(n); + assert_eq!( + bincode::serialize(&n).unwrap(), + bincode::serialize(&amt).unwrap(), + ); + } } diff --git a/sequencer/src/state_signature.rs b/sequencer/src/state_signature.rs index 8e4609e34..d3f2964d3 100644 --- a/sequencer/src/state_signature.rs +++ b/sequencer/src/state_signature.rs @@ -20,11 +20,10 @@ use hotshot_types::{ }, PeerConfig, }; -use jf_primitives::{ - crhf::{VariableLengthRescueCRHF, CRHF}, - errors::PrimitivesError, - signatures::SignatureScheme, -}; +use jf_crhf::CRHF; +use jf_rescue::crhf::VariableLengthRescueCRHF; +use jf_rescue::RescueError; +use jf_signature::SignatureScheme; use std::collections::{HashMap, VecDeque}; use surf_disco::{Client, Url}; use tide_disco::error::ServerError; @@ -77,10 +76,7 @@ impl StateSigner { match form_light_client_state(leaf, &self.stake_table_comm) { Ok(state) => { let signature = self.sign_new_state(&state).await; - tracing::debug!( - "New leaves decided. Latest block height: {}", - leaf.get_height(), - ); + tracing::debug!("New leaves decided. Latest block height: {}", leaf.height(),); if let Some(client) = &self.relay_server_client { let request_body = StateSignatureRequestBody { @@ -138,7 +134,7 @@ impl StateSigner { } } -fn hash_bytes_to_field(bytes: &[u8]) -> Result { +fn hash_bytes_to_field(bytes: &[u8]) -> Result { // make sure that `mod_order` won't happen. let bytes_len = ((::MODULUS_BIT_SIZE + 7) / 8 - 1) as usize; let elem = bytes @@ -151,8 +147,8 @@ fn hash_bytes_to_field(bytes: &[u8]) -> Result { fn form_light_client_state( leaf: &Leaf, stake_table_comm: &StakeTableCommitmentType, -) -> Result { - let header = leaf.get_block_header(); +) -> anyhow::Result { + let header = leaf.block_header(); let mut block_comm_root_bytes = vec![]; header .block_merkle_tree_root @@ -163,8 +159,8 @@ fn form_light_client_state( .fee_merkle_tree_root .serialize_compressed(&mut fee_ledger_comm_bytes)?; Ok(LightClientState { - view_number: leaf.get_view_number().get_u64() as usize, - block_height: leaf.get_height() as usize, + view_number: leaf.view_number().u64() as usize, + block_height: leaf.height() as usize, block_comm_root: hash_bytes_to_field(&block_comm_root_bytes)?, fee_ledger_comm: hash_bytes_to_field(&fee_ledger_comm_bytes)?, stake_table_comm: *stake_table_comm, @@ -204,8 +200,8 @@ pub fn static_stake_table_commitment( known_nodes_with_stakes.iter().for_each(|peer| { // This `unwrap()` won't fail unless number of entries exceeds `capacity` st.register( - *peer.stake_table_entry.get_key(), - peer.stake_table_entry.get_stake(), + *peer.stake_table_entry.key(), + peer.stake_table_entry.stake(), peer.state_ver_key.clone(), ) .unwrap(); diff --git a/sequencer/src/state_signature/relay_server.rs b/sequencer/src/state_signature/relay_server.rs index 8cda5a88e..9c78c7c56 100644 --- a/sequencer/src/state_signature/relay_server.rs +++ b/sequencer/src/state_signature/relay_server.rs @@ -8,7 +8,7 @@ use hotshot_stake_table::vec_based::config::FieldType; use hotshot_types::light_client::{ StateSignature, StateSignatureScheme, StateSignaturesBundle, StateVerKey, }; -use jf_primitives::signatures::SignatureScheme; +use jf_signature::SignatureScheme; use std::{ collections::{BTreeSet, HashMap}, path::PathBuf, @@ -88,7 +88,7 @@ impl StateRelayServerDataSource for StateRelayServerState { match &self.latest_available_bundle { Some(bundle) => Ok(bundle.clone()), None => Err(tide_disco::error::ServerError::catch_all( - StatusCode::NotFound, + StatusCode::NOT_FOUND, "The light client state signatures are not ready.".to_owned(), )), } @@ -115,7 +115,7 @@ impl StateRelayServerDataSource for StateRelayServerState { let state_msg: [FieldType; 7] = (&state).into(); if StateSignatureScheme::verify(&(), &key, state_msg, &signature).is_err() { return Err(tide_disco::error::ServerError::catch_all( - StatusCode::BadRequest, + StatusCode::BAD_REQUEST, "The posted signature is not valid.".to_owned(), )); } @@ -141,7 +141,7 @@ impl StateRelayServerDataSource for StateRelayServerState { std::collections::hash_map::Entry::Occupied(_) => { // A signature is already posted for this key with this state return Err(tide_disco::error::ServerError::catch_all( - StatusCode::BadRequest, + StatusCode::BAD_REQUEST, "A signature of this light client state is already posted at this block height for this key.".to_owned(), )); } @@ -224,7 +224,7 @@ where pub async fn run_relay_server( shutdown_listener: Option>, - threshold: u64, + threshold: U256, url: Url, bind_version: Ver, ) -> std::io::Result<()> { @@ -234,7 +234,6 @@ pub async fn run_relay_server( // We don't have a stake table yet, putting some temporary value here. // Related issue: [https://github.com/EspressoSystems/espresso-sequencer/issues/1022] - let threshold = U256::from(threshold); let state = State::new(StateRelayServerState::new(threshold).with_shutdown_signal(shutdown_listener)); let mut app = App::::with_state(state); diff --git a/sequencer/src/transaction.rs b/sequencer/src/transaction.rs index 801e74884..1df957abe 100644 --- a/sequencer/src/transaction.rs +++ b/sequencer/src/transaction.rs @@ -1,23 +1,34 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use committable::{Commitment, Committable}; -use derive_more::{Display, From, Into}; +use derive_more::Display; +use hotshot_query_service::explorer::ExplorerTransaction; use hotshot_types::traits::block_contents::Transaction as HotShotTransaction; -use jf_primitives::merkle_tree::namespaced_merkle_tree::{Namespace, Namespaced}; -use serde::{Deserialize, Serialize}; +use serde::{de::Error, Deserialize, Deserializer, Serialize}; +/// TODO [`NamespaceId`] has historical debt to repay: +/// - +/// - It must fit into 4 bytes in order to maintain serialization compatibility +/// for [`crate::block::NsTable`], yet it currently occupies 8 bytes in order +/// to maintain [`serde`] serialization compatibility with [`Transaction`]. +/// - Thus, it's a newtype for `u64` that impls `From` and has a manual +/// impl for [`serde::Deserialize`] that deserializes a `u64` but then returns +/// an error if the value cannot fit into a `u32`. This is ugly. In the future +/// we need to break serialization compatibility so that `NsTable` and +/// `Transaction` can agree on the byte length for `NamespaceId` and all this +/// cruft should be removed. +/// - We should move [`NamespaceId`] to `crate::block::full_payload::ns_table` +/// module because that's where it's byte length is dictated, so that's where +/// it makes the most sense to put serialization. See +/// #[derive( Clone, Copy, Serialize, - Deserialize, Debug, Display, PartialEq, Eq, Hash, - Into, - From, - Default, CanonicalDeserialize, CanonicalSerialize, PartialOrd, @@ -26,13 +37,41 @@ use serde::{Deserialize, Serialize}; #[display(fmt = "{_0}")] pub struct NamespaceId(u64); -impl Namespace for NamespaceId { - fn max() -> Self { - Self(u64::max_value()) +impl From for NamespaceId { + fn from(value: u32) -> Self { + Self(value as u64) } +} - fn min() -> Self { - Self(u64::min_value()) +impl From for u32 { + fn from(value: NamespaceId) -> Self { + value.0 as Self + } +} + +impl<'de> Deserialize<'de> for NamespaceId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Unexpected; + + let ns_id = ::deserialize(deserializer)?; + if ns_id > u32::MAX as u64 { + Err(D::Error::invalid_value( + Unexpected::Unsigned(ns_id), + &"at most u32::MAX", + )) + } else { + Ok(NamespaceId(ns_id)) + } + } +} + +impl NamespaceId { + #[cfg(any(test, feature = "testing"))] + pub fn random(rng: &mut dyn rand::RngCore) -> Self { + Self(rng.next_u32() as u64) } } @@ -66,12 +105,24 @@ impl Transaction { &self.payload } + pub fn into_payload(self) -> Vec { + self.payload + } + #[cfg(any(test, feature = "testing"))] pub fn random(rng: &mut dyn rand::RngCore) -> Self { use rand::Rng; let len = rng.gen_range(0..100); Self::new( - NamespaceId(rng.gen_range(0..10)), + NamespaceId::random(rng), + (0..len).map(|_| rand::random::()).collect::>(), + ) + } + #[cfg(any(test, feature = "testing"))] + /// Useful for when we want to test size of transaction(s) + pub fn of_size(len: usize) -> Self { + Self::new( + NamespaceId(1), (0..len).map(|_| rand::random::()).collect::>(), ) } @@ -79,17 +130,10 @@ impl Transaction { impl HotShotTransaction for Transaction {} -impl Namespaced for Transaction { - type Namespace = NamespaceId; - fn get_namespace(&self) -> Self::Namespace { - self.namespace - } -} - impl Committable for Transaction { fn commit(&self) -> Commitment { committable::RawCommitmentBuilder::new("Transaction") - .u64_field("namespace", self.namespace.into()) + .u64_field("namespace", self.namespace.0) .var_size_bytes(&self.payload) .finalize() } @@ -98,3 +142,10 @@ impl Committable for Transaction { "TX".into() } } + +impl ExplorerTransaction for Transaction { + type NamespaceId = NamespaceId; + fn namespace_id(&self) -> Self::NamespaceId { + self.namespace + } +} diff --git a/utils/Cargo.toml b/utils/Cargo.toml index 7dd22b841..bfd401dc0 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -15,8 +15,9 @@ contract-bindings = { path = "../contract-bindings" } derive_more = { workspace = true } ethers = { workspace = true } futures = { workspace = true } -hotshot-contract-adapter ={ path = "../contracts/rust/adapter" } +hotshot-contract-adapter = { workspace = true } portpicker = { workspace = true } +serde = { workspace = true } serde_json = "^1.0.113" surf = "2.3.2" tempfile = "3.9.0" diff --git a/utils/src/deployer.rs b/utils/src/deployer.rs index 204d85196..57be73dc6 100644 --- a/utils/src/deployer.rs +++ b/utils/src/deployer.rs @@ -1,17 +1,23 @@ use anyhow::{ensure, Context}; use async_std::sync::Arc; -use clap::{builder::OsStr, Parser}; +use clap::{builder::OsStr, Parser, ValueEnum}; use contract_bindings::{ - light_client::LIGHTCLIENT_ABI, light_client_mock::LIGHTCLIENTMOCK_ABI, + erc1967_proxy::ERC1967Proxy, + fee_contract::FeeContract, + hot_shot::HotShot, + light_client::{LightClient, LIGHTCLIENT_ABI}, + light_client_mock::LIGHTCLIENTMOCK_ABI, light_client_state_update_vk::LightClientStateUpdateVK, - light_client_state_update_vk_mock::LightClientStateUpdateVKMock, plonk_verifier::PlonkVerifier, + light_client_state_update_vk_mock::LightClientStateUpdateVKMock, + plonk_verifier::PlonkVerifier, shared_types::LightClientState, }; use derive_more::Display; -use ethers::{prelude::*, solc::artifacts::BytecodeObject}; +use ethers::{prelude::*, signers::coins_bip39::English, solc::artifacts::BytecodeObject}; use futures::future::{BoxFuture, FutureExt}; use hotshot_contract_adapter::light_client::ParsedLightClientState; use std::{collections::HashMap, io::Write, ops::Deref}; +use url::Url; /// Set of predeployed contracts. #[derive(Clone, Debug, Parser)] @@ -35,6 +41,14 @@ pub struct DeployedContracts { /// Use an already-deployed LightClient.sol proxy instead of deploying a new one. #[clap(long, env = Contract::LightClientProxy)] light_client_proxy: Option
, + + /// Use an already-deployed FeeContract.sol instead of deploying a new one. + #[clap(long, env = Contract::FeeContract)] + fee_contract: Option
, + + /// Use an already-deployed FeeContract.sol proxy instead of deploying a new one. + #[clap(long, env = Contract::FeeContractProxy)] + fee_contract_proxy: Option
, } /// An identifier for a particular contract. @@ -50,6 +64,10 @@ pub enum Contract { LightClient, #[display(fmt = "ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS")] LightClientProxy, + #[display(fmt = "ESPRESSO_SEQUENCER_FEE_CONTRACT_ADDRESS")] + FeeContract, + #[display(fmt = "ESPRESSO_SEQUENCER_FEE_CONTRACT_PROXY_ADDRESS")] + FeeContractProxy, } impl From for OsStr { @@ -80,11 +98,24 @@ impl From for Contracts { if let Some(addr) = deployed.light_client_proxy { m.insert(Contract::LightClientProxy, addr); } + if let Some(addr) = deployed.fee_contract { + m.insert(Contract::FeeContract, addr); + } + if let Some(addr) = deployed.fee_contract_proxy { + m.insert(Contract::FeeContractProxy, addr); + } Self(m) } } impl Contracts { + pub fn new() -> Self { + Contracts(HashMap::new()) + } + + pub fn get_contract_address(&self, contract: Contract) -> Option
{ + self.0.get(&contract).copied() + } /// Deploy a contract by calling a function. /// /// The `deploy` function will be called only if contract `name` is not already deployed; @@ -270,3 +301,104 @@ pub async fn deploy_mock_light_client_contract( .await?; Ok(contract.address()) } + +pub async fn deploy( + l1url: Url, + mnemonic: String, + account_index: u32, + use_mock_contract: bool, + only: Option>, + genesis: BoxFuture<'_, anyhow::Result>, + mut contracts: Contracts, +) -> anyhow::Result { + let provider = Provider::::try_from(l1url.to_string())?; + let chain_id = provider.get_chainid().await?.as_u64(); + let wallet = MnemonicBuilder::::default() + .phrase(mnemonic.as_str()) + .index(account_index)? + .build()? + .with_chain_id(chain_id); + let owner = wallet.address(); + let l1 = Arc::new(SignerMiddleware::new(provider, wallet)); + + // As a sanity check, check that the deployer address has some balance of ETH it can use to pay + // gas. + let balance = l1.get_balance(owner, None).await?; + ensure!( + balance > 0.into(), + "deployer account {owner:#x} is not funded!" + ); + tracing::info!(%balance, "deploying from address {owner:#x}"); + + // `HotShot.sol` + if should_deploy(ContractGroup::HotShot, &only) { + contracts + .deploy_tx(Contract::HotShot, HotShot::deploy(l1.clone(), ())?) + .await?; + } + + // `LightClient.sol` + if should_deploy(ContractGroup::LightClient, &only) { + // Deploy the upgradable light client contract first, then initialize it through a proxy contract + let lc_address = if use_mock_contract { + contracts + .deploy_fn(Contract::LightClient, |contracts| { + deploy_mock_light_client_contract(l1.clone(), contracts, None).boxed() + }) + .await? + } else { + contracts + .deploy_fn(Contract::LightClient, |contracts| { + deploy_light_client_contract(l1.clone(), contracts).boxed() + }) + .await? + }; + let light_client = LightClient::new(lc_address, l1.clone()); + + let data = light_client + .initialize(genesis.await?.into(), u32::MAX, owner) + .calldata() + .context("calldata for initialize transaction not available")?; + contracts + .deploy_tx( + Contract::LightClientProxy, + ERC1967Proxy::deploy(l1.clone(), (lc_address, data))?, + ) + .await?; + } + + // `FeeContract.sol` + if should_deploy(ContractGroup::FeeContract, &only) { + let fee_contract_address = contracts + .deploy_tx(Contract::FeeContract, FeeContract::deploy(l1.clone(), ())?) + .await?; + let fee_contract = FeeContract::new(fee_contract_address, l1.clone()); + let data = fee_contract + .initialize(owner) + .calldata() + .context("calldata for initialize transaction not available")?; + contracts + .deploy_tx( + Contract::FeeContractProxy, + ERC1967Proxy::deploy(l1.clone(), (fee_contract_address, data))?, + ) + .await?; + } + + Ok(contracts) +} + +fn should_deploy(group: ContractGroup, only: &Option>) -> bool { + match only { + Some(groups) => groups.contains(&group), + None => true, + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)] +pub enum ContractGroup { + #[clap(name = "hotshot")] + HotShot, + FeeContract, + LightClient, +} diff --git a/utils/src/lib.rs b/utils/src/lib.rs index f0214d2e9..b3ecadee4 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -21,6 +21,7 @@ use tempfile::TempDir; use url::Url; pub mod deployer; +pub mod ser; pub mod test_utils; pub type Signer = SignerMiddleware, LocalWallet>; @@ -72,22 +73,7 @@ impl AnvilOptions { }; // When we are running a local Anvil node, as in tests, some endpoints (e.g. eth_feeHistory) - // do not work until at least one block has been mined. Send a transaction to force the - // mining of a block. - anvil - .provider() - .send_transaction( - TransactionRequest { - to: Some(Address::zero().into()), - ..Default::default() - }, - None, - ) - .await - .unwrap() - .await - .unwrap(); - + // do not work until at least one block has been mined. while let Err(err) = anvil .provider() .fee_history(1, BlockNumber::Latest, &[]) diff --git a/utils/src/ser.rs b/utils/src/ser.rs new file mode 100644 index 000000000..63c4a728a --- /dev/null +++ b/utils/src/ser.rs @@ -0,0 +1,89 @@ +use serde::{ + de::{DeserializeOwned, Deserializer, Error as _}, + ser::{Error as _, Serializer}, + Deserialize, Serialize, +}; + +/// Types which can be deserialized from either integers or strings. +/// +/// Some types can be represented as an integer or a string in human-readable formats like JSON or +/// TOML. For example, 1 GWEI might be represented by the integer `1000000000` or the string `"1 +/// gwei"`. Such types can implement `FromStringOrInteger` and then use [`impl_string_or_integer`] +/// to derive this user-friendly serialization. +/// +/// These types are assumed to have an efficient representation as an integral type in Rust -- +/// [`Self::Binary`] -- and will be serialized to and from this type when using a non-human-readable +/// encoding. With human readable encodings, serialization is always to a string. +pub trait FromStringOrInteger: Sized { + type Binary: Serialize + DeserializeOwned; + type Integer: Serialize + DeserializeOwned; + + fn from_binary(b: Self::Binary) -> anyhow::Result; + fn from_string(s: String) -> anyhow::Result; + fn from_integer(i: Self::Integer) -> anyhow::Result; + + fn to_binary(&self) -> anyhow::Result; + fn to_string(&self) -> anyhow::Result; +} + +/// Deserialize a type from either a string or integer in human-readable encodings. +/// +/// This macro implements serde `Serialize` and `DeserializeOwned` traits with a friendly +/// deserialization mechanism that can handle strings and integers when using human-readable +/// formats. It works with any [`FromStringOrInteger`] type. +#[macro_export] +macro_rules! impl_serde_from_string_or_integer { + ($t:ty) => { + impl serde::Serialize for $t { + fn serialize(&self, s: S) -> Result { + $crate::ser::string_or_integer::serialize(self, s) + } + } + + impl<'de> serde::Deserialize<'de> for $t { + fn deserialize>(d: D) -> Result { + $crate::ser::string_or_integer::deserialize(d) + } + } + }; +} +pub use crate::impl_serde_from_string_or_integer; + +/// Deserialize a type from either a string or integer in human-readable encodings. +/// +/// This serialization module can be used with any [`FromStringOrInteger`] type. It is usually used +/// only indirectly by the expansion of the [`impl_string_or_integer`] macro. +pub mod string_or_integer { + use super::*; + + #[derive(Debug, Deserialize)] + #[serde(untagged)] + enum StringOrInteger { + String(String), + Integer(I), + } + + pub fn serialize( + t: &T, + s: S, + ) -> Result { + if s.is_human_readable() { + t.to_string().map_err(S::Error::custom)?.serialize(s) + } else { + t.to_binary().map_err(S::Error::custom)?.serialize(s) + } + } + + pub fn deserialize<'a, T: FromStringOrInteger, D: Deserializer<'a>>( + d: D, + ) -> Result { + if d.is_human_readable() { + match StringOrInteger::deserialize(d)? { + StringOrInteger::String(s) => T::from_string(s).map_err(D::Error::custom), + StringOrInteger::Integer(i) => T::from_integer(i).map_err(D::Error::custom), + } + } else { + T::from_binary(T::Binary::deserialize(d)?).map_err(D::Error::custom) + } + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..0cc19783c --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4884 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@adraffy/ens-normalize@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + +"@babel/compat-data@^7.23.5": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" + integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.5.tgz#15ab5b98e101972d171aeef92ac70d8d6718f06a" + integrity sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.24.5" + "@babel/helpers" "^7.24.5" + "@babel/parser" "^7.24.5" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.5" + "@babel/types" "^7.24.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.24.5", "@babel/generator@^7.7.2": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.5.tgz#e5afc068f932f05616b66713e28d0f04e99daeb3" + integrity sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA== + dependencies: + "@babel/types" "^7.24.5" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.24.3": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" + integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== + dependencies: + "@babel/types" "^7.24.0" + +"@babel/helper-module-transforms@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz#ea6c5e33f7b262a0ae762fd5986355c45f54a545" + integrity sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.24.3" + "@babel/helper-simple-access" "^7.24.5" + "@babel/helper-split-export-declaration" "^7.24.5" + "@babel/helper-validator-identifier" "^7.24.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a" + integrity sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ== + +"@babel/helper-simple-access@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz#50da5b72f58c16b07fbd992810be6049478e85ba" + integrity sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ== + dependencies: + "@babel/types" "^7.24.5" + +"@babel/helper-split-export-declaration@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz#b9a67f06a46b0b339323617c8c6213b9055a78b6" + integrity sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q== + dependencies: + "@babel/types" "^7.24.5" + +"@babel/helper-string-parser@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" + integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== + +"@babel/helper-validator-identifier@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62" + integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA== + +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helpers@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.5.tgz#fedeb87eeafa62b621160402181ad8585a22a40a" + integrity sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.5" + "@babel/types" "^7.24.5" + +"@babel/highlight@^7.24.2": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.5.tgz#bc0613f98e1dd0720e99b2a9ee3760194a704b6e" + integrity sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790" + integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz#3f6ca04b8c841811dbc3c5c5f837934e0d626c10" + integrity sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844" + integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/template@^7.22.15", "@babel/template@^7.24.0", "@babel/template@^7.3.3": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + +"@babel/traverse@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.5.tgz#972aa0bc45f16983bf64aa1f877b2dd0eea7e6f8" + integrity sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA== + dependencies: + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.24.5" + "@babel/parser" "^7.24.5" + "@babel/types" "^7.24.5" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.24.5", "@babel/types@^7.3.3": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7" + integrity sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ== + dependencies: + "@babel/helper-string-parser" "^7.24.1" + "@babel/helper-validator-identifier" "^7.24.5" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@ethereumjs/common@2.6.5", "@ethereumjs/common@^2.6.4": + version "2.6.5" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" + integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.5" + +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== + +"@ethereumjs/tx@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.2.tgz#197b9b6299582ad84f9527ca961466fce2296c1c" + integrity sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw== + dependencies: + "@ethereumjs/common" "^2.6.4" + ethereumjs-util "^7.1.5" + +"@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" + integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" + +"@ethersproject/abi@^5.6.3": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== + dependencies: + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" + +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== + dependencies: + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + slash "^3.0.0" + +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/curves@1.3.0", "@noble/curves@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" + integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA== + dependencies: + "@noble/hashes" "1.3.3" + +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + +"@noble/hashes@1.3.3", "@noble/hashes@~1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@noble/hashes@^1.3.3", "@noble/hashes@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@safe-global/api-kit@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@safe-global/api-kit/-/api-kit-2.3.1.tgz#be2fdc3bda060500e6a56a680b63a7e7eb365514" + integrity sha512-yetBsgOtTFpUSv6GHe18OKCK/j0ePLUvyHbDNxPU0aEkxBXzku1LoS39jeiOvEqDZTBq5tZwskmwuw3zlUkH8w== + dependencies: + "@safe-global/protocol-kit" "^3.1.0" + "@safe-global/safe-core-sdk-types" "^4.1.0" + ethers "^6.7.1" + node-fetch "^2.7.0" + +"@safe-global/protocol-kit@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@safe-global/protocol-kit/-/protocol-kit-3.1.0.tgz#429f3f11b3b2c60ed31568f853393155f316316b" + integrity sha512-PUZgTohIoQ1KN7RYE2IcQL8lj/LAb4WRgkDwB+1Vv7AoOLTI1U0Whajfe6Ur9w35BrQwr/x2HAoQvVSzH4FZ3Q== + dependencies: + "@noble/hashes" "^1.3.3" + "@safe-global/safe-deployments" "^1.35.0" + ethereumjs-util "^7.1.5" + ethers "^6.7.1" + semver "^7.5.4" + web3 "^1.10.3" + web3-core "^1.10.3" + web3-utils "^1.10.3" + +"@safe-global/safe-core-sdk-types@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk-types/-/safe-core-sdk-types-4.1.0.tgz#5ccee8ffa53cd5d86dde2b16315e13d54d355746" + integrity sha512-/tdHUzlJAOWQEr/K98ogrlkKS8RQuj7ktMx0ep5X9/P9PdIkQ2v7+t27WIxWF4+snThmEwpHDZN+m7yUFAkmiQ== + dependencies: + "@safe-global/safe-deployments" "^1.35.0" + ethers "^6.7.1" + web3-core "^1.10.3" + web3-utils "^1.10.3" + +"@safe-global/safe-deployments@^1.35.0": + version "1.36.0" + resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.36.0.tgz#7e5cd470cc1042182d47f65b59831a64ed8feff1" + integrity sha512-9MbDJveRR64AbmzjIpuUqmDBDtOZpXpvkyhTUs+5UOPT3WgSO375/ZTO7hZpywP7+EmxnjkGc9EoxjGcC4TAyw== + dependencies: + semver "^7.6.0" + +"@scure/base@~1.1.4": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d" + integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g== + +"@scure/bip32@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8" + integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ== + dependencies: + "@noble/curves" "~1.3.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" + +"@scure/bip39@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527" + integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA== + dependencies: + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" + integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== + dependencies: + "@babel/types" "^7.20.7" + +"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + dependencies: + "@types/node" "*" + +"@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + +"@types/graceful-fs@^4.1.3": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== + dependencies: + "@types/node" "*" + +"@types/http-cache-semantics@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^29.5.12": + version "29.5.12" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" + integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "20.12.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050" + integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw== + dependencies: + undici-types "~5.26.4" + +"@types/node@18.15.13": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== + +"@types/node@^12.12.6": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/pbkdf2@^3.0.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.2.tgz#2dc43808e9985a2c69ff02e2d2027bd4fe33e8dc" + integrity sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew== + dependencies: + "@types/node" "*" + +"@types/responselike@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" + integrity sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ== + dependencies: + "@types/node" "*" + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + +abortcontroller-polyfill@^1.7.5: + version "1.7.5" + resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" + integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-walk@^8.1.1: + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + +acorn@^8.4.1: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +aes-js@4.0.0-beta.5: + version "4.0.0-beta.5" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" + integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== + +ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^3.0.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" + integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== + +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== + dependencies: + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +bignumber.js@^9.0.0: + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + +bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== + +bn.js@^4.11.6, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +body-parser@1.20.2, body-parser@^1.16.0: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserslist@^4.22.2: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== + dependencies: + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.8" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" + integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== + dependencies: + node-gyp-build "^4.3.0" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-lookup@^6.0.4: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" + integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + +call-bind@^1.0.2, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001587: + version "1.0.30001618" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz#fad74fa006aef0f01e8e5c0a5540c74d8d36ec6f" + integrity sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +chownr@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +cjs-module-lexer@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" + integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-fetch@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +d@1, d@^1.0.1, d@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== + dependencies: + mimic-response "^1.0.0" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +dedent@^1.0.0: + version "1.5.3" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.668: + version "1.4.767" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.767.tgz#b885cfefda5a2e7a7ee356c567602012294ed260" + integrity sha512-nzzHfmQqBss7CE3apQHkHjXW77+8w3ubGCIoEijKCJebPufREaFETgGXWTkh32t259F3Kcq+R8MZdFdOJROgYw== + +elliptic@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +elliptic@^6.4.0, elliptic@^6.5.4: + version "6.5.5" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" + integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es5-ext@^0.10.35, es5-ext@^0.10.62, es5-ext@^0.10.63, es5-ext@^0.10.64, es5-ext@~0.10.14: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-promise@^4.2.8: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== + dependencies: + d "^1.0.2" + ext "^1.7.0" + +escalade@^3.1.1, escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eth-ens-namehash@2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw== + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +ethereum-bloom-filters@^1.0.6: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.1.0.tgz#b3fc1eb789509ee30db0bf99a2988ccacb8d0397" + integrity sha512-J1gDRkLpuGNvWYzWslBQR9cDV4nd4kfvVTE/Wy4Kkm4yb3EYRSlyi0eB/inTsSTTVyA0+HyzHgbr95Fn/Z1fSw== + dependencies: + "@noble/hashes" "^1.4.0" + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a" + integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA== + dependencies: + "@noble/curves" "1.3.0" + "@noble/hashes" "1.3.3" + "@scure/bip32" "1.3.3" + "@scure/bip39" "1.2.2" + +ethereumjs-util@^7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethers@^6.12.1, ethers@^6.7.1: + version "6.12.1" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.12.1.tgz#517ff6d66d4fd5433e38e903051da3e57c87ff37" + integrity sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "18.15.13" + aes-js "4.0.0-beta.5" + tslib "2.4.0" + ws "8.5.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +express@^4.14.0: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data-encoder@1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96" + integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0, get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + +glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +got@12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-12.1.0.tgz#099f3815305c682be4fd6b0ee0726d8e4c6b0af4" + integrity sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig== + dependencies: + "@sindresorhus/is" "^4.6.0" + "@szmarczak/http-timer" "^5.0.1" + "@types/cacheable-request" "^6.0.2" + "@types/responselike" "^1.0.0" + cacheable-lookup "^6.0.4" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + form-data-encoder "1.7.1" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^2.0.0" + +got@^11.8.5: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg== + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-instrument@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1" + integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== + dependencies: + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" + +jest-util@^29.0.0, jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^1.2.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +keccak@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +keyv@^4.0.0: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@1.x, make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== + dependencies: + dom-walk "^0.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^2.6.0, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w== + dependencies: + mkdirp "*" + +mkdirp@*: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@^2.6.12, node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.8.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" + integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA== + dependencies: + http-https "^1.0.0" + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-headers@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" + integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +pbkdf2@^3.0.17: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picocolors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +psl@^1.1.28: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +pure-rand@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +qs@~6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +request@^2.79.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.2" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.2.tgz#5708fb0919d440657326cd5fe7d2599d07705019" + integrity sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +sshpk@^1.7.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== + dependencies: + is-hex-prefixed "1.0.0" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +swarm-js@^0.1.40: + version "0.1.42" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.42.tgz#497995c62df6696f6e22372f457120e43e727979" + integrity sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^11.8.5" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +tar@^4.0.2: + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +ts-jest@^29.1.2: + version "29.1.3" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.3.tgz#2bab16ba5ab0f4896684985f9618acc2cf1197e9" + integrity sha512-6L9qz3ginTd1NKhOxmkP0qU3FyKjj5CPoY+anszfVn6Pmv/RIKzhiMCsH7Yb7UvJR9I2A64rm4zQl531s2F1iw== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "^7.5.3" + yargs-parser "^21.0.1" + +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typescript@^5.4.5: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.13: + version "1.0.15" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz#60ed9f8cba4a728b7ecf7356f641a31e3a691d97" + integrity sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== + +utf-8-validate@^5.0.2: + version "5.0.10" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" + integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== + dependencies: + node-gyp-build "^4.3.0" + +utf8@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-to-istanbul@^9.0.1: + version "9.2.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" + integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^2.0.0" + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +web3-bzz@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.10.4.tgz#dcc787970767d9004c73d11d0eeef774ce16b880" + integrity sha512-ZZ/X4sJ0Uh2teU9lAGNS8EjveEppoHNQiKlOXAjedsrdWuaMErBPdLQjXfcrYvN6WM6Su9PMsAxf3FXXZ+HwQw== + dependencies: + "@types/node" "^12.12.6" + got "12.1.0" + swarm-js "^0.1.40" + +web3-core-helpers@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.10.4.tgz#bd2b4140df2016d5dd3bb2b925fc29ad8678677c" + integrity sha512-r+L5ylA17JlD1vwS8rjhWr0qg7zVoVMDvWhajWA5r5+USdh91jRUYosp19Kd1m2vE034v7Dfqe1xYRoH2zvG0g== + dependencies: + web3-eth-iban "1.10.4" + web3-utils "1.10.4" + +web3-core-method@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.10.4.tgz#566b52f006d3cbb13b21b72b8d2108999bf5d6bf" + integrity sha512-uZTb7flr+Xl6LaDsyTeE2L1TylokCJwTDrIVfIfnrGmnwLc6bmTWCCrm71sSrQ0hqs6vp/MKbQYIYqUN0J8WyA== + dependencies: + "@ethersproject/transactions" "^5.6.2" + web3-core-helpers "1.10.4" + web3-core-promievent "1.10.4" + web3-core-subscriptions "1.10.4" + web3-utils "1.10.4" + +web3-core-promievent@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.10.4.tgz#629b970b7934430b03c5033c79f3bb3893027e22" + integrity sha512-2de5WnJQ72YcIhYwV/jHLc4/cWJnznuoGTJGD29ncFQHAfwW/MItHFSVKPPA5v8AhJe+r6y4Y12EKvZKjQVBvQ== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.10.4.tgz#eb1f147e6b9df84e3a37e602162f8925bdb4bb9a" + integrity sha512-vqP6pKH8RrhT/2MoaU+DY/OsYK9h7HmEBNCdoMj+4ZwujQtw/Mq2JifjwsJ7gits7Q+HWJwx8q6WmQoVZAWugg== + dependencies: + util "^0.12.5" + web3-core-helpers "1.10.4" + web3-providers-http "1.10.4" + web3-providers-ipc "1.10.4" + web3-providers-ws "1.10.4" + +web3-core-subscriptions@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.10.4.tgz#2f4dcb404237e92802a563265d11a33934dc38e6" + integrity sha512-o0lSQo/N/f7/L76C0HV63+S54loXiE9fUPfHFcTtpJRQNDBVsSDdWRdePbWwR206XlsBqD5VHApck1//jEafTw== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.10.4" + +web3-core@1.10.4, web3-core@^1.10.3: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.4.tgz#639de68b8b9871d2dc8892e0dd4e380cb1361a98" + integrity sha512-B6elffYm81MYZDTrat7aEhnhdtVE3lDBUZft16Z8awYMZYJDbnykEbJVS+l3mnA7AQTnSDr/1MjWofGDLBJPww== + dependencies: + "@types/bn.js" "^5.1.1" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-core-requestmanager "1.10.4" + web3-utils "1.10.4" + +web3-eth-abi@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.10.4.tgz#16c19d0bde0aaf8c1a56cb7743a83156d148d798" + integrity sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ== + dependencies: + "@ethersproject/abi" "^5.6.3" + web3-utils "1.10.4" + +web3-eth-accounts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.4.tgz#df30e85a7cd70e475f8cf52361befba408829e34" + integrity sha512-ysy5sVTg9snYS7tJjxVoQAH6DTOTkRGR8emEVCWNGLGiB9txj+qDvSeT0izjurS/g7D5xlMAgrEHLK1Vi6I3yg== + dependencies: + "@ethereumjs/common" "2.6.5" + "@ethereumjs/tx" "3.5.2" + "@ethereumjs/util" "^8.1.0" + eth-lib "0.2.8" + scrypt-js "^3.0.1" + uuid "^9.0.0" + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-utils "1.10.4" + +web3-eth-contract@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.10.4.tgz#22d39f04e11d9ff4e726e8025a56d78e843a2c3d" + integrity sha512-Q8PfolOJ4eV9TvnTj1TGdZ4RarpSLmHnUnzVxZ/6/NiTfe4maJz99R0ISgwZkntLhLRtw0C7LRJuklzGYCNN3A== + dependencies: + "@types/bn.js" "^5.1.1" + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-core-promievent "1.10.4" + web3-core-subscriptions "1.10.4" + web3-eth-abi "1.10.4" + web3-utils "1.10.4" + +web3-eth-ens@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.10.4.tgz#3d991adac52bc8e598f1f1b8528337fa6291004c" + integrity sha512-LLrvxuFeVooRVZ9e5T6OWKVflHPFgrVjJ/jtisRWcmI7KN/b64+D/wJzXqgmp6CNsMQcE7rpmf4CQmJCrTdsgg== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-promievent "1.10.4" + web3-eth-abi "1.10.4" + web3-eth-contract "1.10.4" + web3-utils "1.10.4" + +web3-eth-iban@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.10.4.tgz#bc61b4a1930d19b1df8762c606d669902558e54d" + integrity sha512-0gE5iNmOkmtBmbKH2aTodeompnNE8jEyvwFJ6s/AF6jkw9ky9Op9cqfzS56AYAbrqEFuClsqB/AoRves7LDELw== + dependencies: + bn.js "^5.2.1" + web3-utils "1.10.4" + +web3-eth-personal@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.10.4.tgz#e2ee920f47e84848288e03442659cdbb2c4deea2" + integrity sha512-BRa/hs6jU1hKHz+AC/YkM71RP3f0Yci1dPk4paOic53R4ZZG4MgwKRkJhgt3/GPuPliwS46f/i5A7fEGBT4F9w== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-net "1.10.4" + web3-utils "1.10.4" + +web3-eth@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.10.4.tgz#3a908c635cb5d935bd30473e452f3bd7f2ee66a5" + integrity sha512-Sql2kYKmgt+T/cgvg7b9ce24uLS7xbFrxE4kuuor1zSCGrjhTJ5rRNG8gTJUkAJGKJc7KgnWmgW+cOfMBPUDSA== + dependencies: + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-core-subscriptions "1.10.4" + web3-eth-abi "1.10.4" + web3-eth-accounts "1.10.4" + web3-eth-contract "1.10.4" + web3-eth-ens "1.10.4" + web3-eth-iban "1.10.4" + web3-eth-personal "1.10.4" + web3-net "1.10.4" + web3-utils "1.10.4" + +web3-net@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.4.tgz#20e12c60e4477d4298979d8d5d66b9abf8e66a09" + integrity sha512-mKINnhOOnZ4koA+yV2OT5s5ztVjIx7IY9a03w6s+yao/BUn+Luuty0/keNemZxTr1E8Ehvtn28vbOtW7Ids+Ow== + dependencies: + web3-core "1.10.4" + web3-core-method "1.10.4" + web3-utils "1.10.4" + +web3-providers-http@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.10.4.tgz#ca7aa58aeaf8123500c24ffe0595896319f830e8" + integrity sha512-m2P5Idc8hdiO0l60O6DSCPw0kw64Zgi0pMjbEFRmxKIck2Py57RQMu4bxvkxJwkF06SlGaEQF8rFZBmuX7aagQ== + dependencies: + abortcontroller-polyfill "^1.7.5" + cross-fetch "^4.0.0" + es6-promise "^4.2.8" + web3-core-helpers "1.10.4" + +web3-providers-ipc@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.10.4.tgz#2e03437909e4e7771d646ff05518efae44b783c3" + integrity sha512-YRF/bpQk9z3WwjT+A6FI/GmWRCASgd+gC0si7f9zbBWLXjwzYAKG73bQBaFRAHex1hl4CVcM5WUMaQXf3Opeuw== + dependencies: + oboe "2.1.5" + web3-core-helpers "1.10.4" + +web3-providers-ws@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.10.4.tgz#55d0c3ba36c6a79d105f02e20a707eb3978e7f82" + integrity sha512-j3FBMifyuFFmUIPVQR4pj+t5ILhAexAui0opgcpu9R5LxQrLRUZxHSnU+YO25UycSOa/NAX8A+qkqZNpcFAlxA== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.10.4" + websocket "^1.0.32" + +web3-shh@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.10.4.tgz#9852d6f3d05678e31e49235a60fea10ca7a9e21d" + integrity sha512-cOH6iFFM71lCNwSQrC3niqDXagMqrdfFW85hC9PFUrAr3PUrIem8TNstTc3xna2bwZeWG6OBy99xSIhBvyIACw== + dependencies: + web3-core "1.10.4" + web3-core-method "1.10.4" + web3-core-subscriptions "1.10.4" + web3-net "1.10.4" + +web3-utils@1.10.4, web3-utils@^1.10.3: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec" + integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A== + dependencies: + "@ethereumjs/util" "^8.1.0" + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereum-cryptography "^2.1.2" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3@^1.10.3: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.10.4.tgz#5d5e59b976eaf758b060fe1a296da5fe87bdc79c" + integrity sha512-kgJvQZjkmjOEKimx/tJQsqWfRDPTTcBfYPa9XletxuHLpHcXdx67w8EFn5AW3eVxCutE9dTVHgGa9VYe8vgsEA== + dependencies: + web3-bzz "1.10.4" + web3-core "1.10.4" + web3-eth "1.10.4" + web3-eth-personal "1.10.4" + web3-net "1.10.4" + web3-shh "1.10.4" + web3-utils "1.10.4" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +websocket@^1.0.32: + version "1.0.35" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.35.tgz#374197207d7d4cc4c36cbf8a1bb886ee52a07885" + integrity sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.63" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-typed-array@^1.1.14, which-typed-array@^1.1.2: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" + integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr@^2.0.4, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==