diff --git a/.claude/agents/network-logs.md b/.claude/agents/network-logs.md index 2a5df608e37d..b0dc08117ba1 100644 --- a/.claude/agents/network-logs.md +++ b/.claude/agents/network-logs.md @@ -91,7 +91,8 @@ Pods follow the pattern `{namespace}-{component}-{index}`: ## Deployment-Specific Notes -- **next-net** redeploys every morning at ~4am UTC. Always use timestamp range filters (not `--freshness`) when querying next-net for a specific date, and expect logs to only cover a single instance of the network. +- **next-net** redeploys every morning at ~4am UTC. Always use timestamp range filters (not `--freshness`) when querying next-net for a specific date, and expect logs to only cover a single instance of the network. Because next-net resets daily, its block height should start near 0 after ~4am UTC. If you are running a morning healthcheck and the block height is unexpectedly large (e.g., hundreds or thousands), flag this as an error — it likely means the nightly redeploy failed and the network is running a stale instance. +- **mainnet** does not run sequencer validators. Instead, it runs infrastructure in **fisherman mode**: nodes simulate building a block for every slot but never actually submit the L1 transaction. This means you will see "built block" or similar messages but no "Published checkpoint" or L1 submission logs. Errors with hash `0xf3e591ac` are a known artifact of fisherman mode and are safe to ignore. ## Filter Building @@ -203,6 +204,7 @@ gcloud logging read ' NOT jsonPayload.message=~"No active peers" NOT jsonPayload.message=~"Not enough txs" NOT jsonPayload.message=~"StateView contract not found" + NOT jsonPayload.message=~"[Bb]lob" ' --limit=100 --format='table[no-heading](timestamp.date("%H:%M:%S"), resource.labels.pod_name, jsonPayload.severity, jsonPayload.module, jsonPayload.message.slice(0,180))' --freshness= --project= ``` @@ -291,6 +293,20 @@ Then synthesize into a single status report covering: This is the most common query pattern — prefer this composite approach over individual queries when the user asks for general status. +### 11. Multi-Network Healthcheck + +When the user asks for a healthcheck across multiple networks (e.g., "how are all the networks doing?"), query each network in parallel and present results as a **summary table**: + +``` +| Network | Status | Block Height | Last Block | Proving | Notes | +|-----------|--------|--------------|------------|---------|-------| +| testnet | ✅ OK | 1234 | 2m ago | Epoch 5 | — | +| next-net | ✅ OK | 45 | 1m ago | Epoch 1 | — | +| mainnet | ✅ OK | 890 | 3m ago | N/A | Fisherman mode | +``` + +Use ✅ for healthy, ⚠️ for degraded, ❌ for down/erroring. Follow the table with brief per-network details only if there are issues worth calling out. Remember deployment-specific notes: next-net resets daily (check block height is reasonable for time of day), mainnet runs in fisherman mode (no L1 submissions, `0xf3e591ac` errors are expected). + ## Known Noise Patterns These patterns appear frequently and are usually harmless — exclude or downplay them: @@ -302,6 +318,8 @@ These patterns appear frequently and are usually harmless — exclude or downpla - `No active peers to send requests to` — P2P reqresp on isolated nodes (e.g., blob-sink) - `Not enough txs to build block` — Normal when transaction volume is low - `StateView contract not found` — Price oracle warning; Uniswap V4 StateView only exists on mainnet, so all other networks emit this. Safe to ignore unless namespace is `mainnet` +- **Blob-related errors** — Errors related to blobs (e.g., blob fetching failures, blob unavailability) are generally expected and safe to ignore. Since the Fusaka hard fork, regular consensus nodes can no longer serve blob data, and we run a couple of these nodes. Exclude or downplay blob errors unless the user is specifically investigating blob issues. +- `0xf3e591ac` — Fisherman mode error on mainnet. Safe to ignore (see Deployment-Specific Notes). ## Reference Tool diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 000000000000..9ddcfd61b65a --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,11 @@ +{ + "permissions": { + "allow": [ + "Bash(gh pr:*)", + "Bash(curl -s -X POST https://ethereum-sepolia-rpc.publicnode.com -H \"Content-Type: application/json\" -d '{\"\"\"\"jsonrpc\"\"\"\":\"\"\"\"2.0\"\"\"\",\"\"\"\"method\"\"\"\":\"\"\"\"eth_getBlockByNumber\"\"\"\",\"\"\"\"params\"\"\"\":[\"\"\"\"finalized\"\"\"\",false],\"\"\"\"id\"\"\"\":1}')", + "Bash(curl -s -X POST https://ethereum-rpc.publicnode.com -H \"Content-Type: application/json\" -d '{\"\"\"\"jsonrpc\"\"\"\":\"\"\"\"2.0\"\"\"\",\"\"\"\"method\"\"\"\":\"\"\"\"eth_getBlockByNumber\"\"\"\",\"\"\"\"params\"\"\"\":[\"\"\"\"finalized\"\"\"\",false],\"\"\"\"id\"\"\"\":1}')", + "Bash(curl -s -X POST https://ethereum-sepolia-rpc.publicnode.com -H \"Content-Type: application/json\" -d '{\"\"\"\"jsonrpc\"\"\"\":\"\"\"\"2.0\"\"\"\",\"\"\"\"method\"\"\"\":\"\"\"\"eth_getBlockByNumber\"\"\"\",\"\"\"\"params\"\"\"\":[\"\"\"\"latest\"\"\"\",false],\"\"\"\"id\"\"\"\":1}')", + "Bash(curl -s -X POST https://ethereum-rpc.publicnode.com -H \"Content-Type: application/json\" -d '{\"\"\"\"jsonrpc\"\"\"\":\"\"\"\"2.0\"\"\"\",\"\"\"\"method\"\"\"\":\"\"\"\"eth_getBlockByNumber\"\"\"\",\"\"\"\"params\"\"\"\":[\"\"\"\"latest\"\"\"\",false],\"\"\"\"id\"\"\"\":1}')" + ] + } +} diff --git a/.github-new/workflows/copy-bb-release-artifacts.yml b/.github-new/workflows/copy-bb-release-artifacts.yml new file mode 100644 index 000000000000..3448935bfa3e --- /dev/null +++ b/.github-new/workflows/copy-bb-release-artifacts.yml @@ -0,0 +1,71 @@ +name: Copy BB Release Artifacts +# When a release is published on aztec-packages, copy barretenberg artifacts +# from AztecProtocol/barretenberg to the aztec-packages release. +# Historical bbup versions (~before April 2026) benefit from having these artifacts here. +# Newer bbup versions are able to download experimental nightlies from barretenberg repo. +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag to copy artifacts for (e.g. v0.82.0)" + required: true + +permissions: + contents: write + +jobs: + copy-artifacts: + runs-on: ubuntu-latest + steps: + - name: Determine tag + id: tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT" + else + echo "tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT" + fi + + - name: Download artifacts from AztecProtocol/barretenberg + env: + GH_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }} + run: | + tag="${{ steps.tag.outputs.tag }}" + echo "Downloading artifacts for tag: $tag" + mkdir -p /tmp/bb-artifacts + + # Download all release assets from the barretenberg repo + if ! gh release download "$tag" \ + --repo AztecProtocol/barretenberg \ + --dir /tmp/bb-artifacts; then + echo "::error::No release found for $tag in AztecProtocol/barretenberg" + exit 1 + fi + + echo "Downloaded artifacts:" + ls -la /tmp/bb-artifacts/ + + - name: Upload artifacts to aztec-packages release + env: + GH_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }} + run: | + tag="${{ steps.tag.outputs.tag }}" + + # Ensure the release exists on aztec-packages (it should if triggered by release event, + # but for workflow_dispatch we may need to verify) + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + if ! gh release view "$tag" --repo AztecProtocol/aztec-packages &>/dev/null; then + echo "::error::No release found for $tag in AztecProtocol/aztec-packages. Create the release first, then re-run." + exit 1 + fi + fi + + echo "Uploading artifacts to aztec-packages release $tag" + gh release upload "$tag" \ + --repo AztecProtocol/aztec-packages \ + --clobber \ + /tmp/bb-artifacts/* + + echo "Done! Artifacts copied to https://github.com/AztecProtocol/aztec-packages/releases/tag/$tag" diff --git a/.github/workflows/ci3-external.yml b/.github/workflows/ci3-external.yml index afbcc7deb840..832d16d52181 100644 --- a/.github/workflows/ci3-external.yml +++ b/.github/workflows/ci3-external.yml @@ -41,7 +41,7 @@ jobs: PR_BASE_REF: ${{ github.event.pull_request.base.ref }} PR_NUMBER: ${{ github.event.pull_request.number }} HAS_CI_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'ci-external') || github.event.label.name == 'ci-external-once' }} - GH_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }} + GH_TOKEN: ${{ github.token }} run: | set -o pipefail git fetch origin "$PR_BASE_REF" --depth=1 &>/dev/null @@ -68,7 +68,7 @@ jobs: MERGE_GROUP_BASE_REF: ${{ github.event.merge_group.base_ref }} PR_BASE_REF: ${{ github.event.pull_request.base.ref }} GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} PR_LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }} run: | # Parse labels from JSON env var to avoid shell injection via label names @@ -85,7 +85,7 @@ jobs: # creds for being able to upload to cache. AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - GITHUB_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} BUILD_INSTANCE_SSH_KEY: ${{ secrets.BUILD_INSTANCE_SSH_KEY }} # DO NOT allow build instance key access to external jobs. CI_USE_BUILD_INSTANCE_KEY: "0" @@ -104,7 +104,7 @@ jobs: # For updating success cache. AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - GITHUB_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} PR_NUMBER: ${{ github.event.pull_request.number }} PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} PR_BASE_REF: ${{ github.event.pull_request.base.ref }} diff --git a/.github/workflows/ci3.yml b/.github/workflows/ci3.yml index 03f02cc8ac19..b139b66eb3c1 100644 --- a/.github/workflows/ci3.yml +++ b/.github/workflows/ci3.yml @@ -173,10 +173,9 @@ jobs: fail-fast: false matrix: test_set: ["1", "2"] - # We either run after a release (tag starting with v), or when the ci-network-scenario label is present in a PR. - # We exclude ci-release-pr test tags (v0.0.1-commit.*) which are only for testing the release process. + # We run on nightly tags only, or when the ci-network-scenario label is present in a PR. needs: ci - if: github.event.pull_request.head.repo.fork != true && github.event.pull_request.draft == false && ((startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-commit.')) || contains(github.event.pull_request.labels.*.name, 'ci-network-scenario')) + if: github.event.pull_request.head.repo.fork != true && github.event.pull_request.draft == false && ((startsWith(github.ref, 'refs/tags/v') && contains(github.ref_name, '-nightly.')) || contains(github.event.pull_request.labels.*.name, 'ci-network-scenario')) steps: - name: Remove label (one-time use) if: github.event.pull_request && contains(github.event.pull_request.labels.*.name, 'ci-network-scenario') diff --git a/.github/workflows/deploy-network.yml b/.github/workflows/deploy-network.yml index fbe634adef15..c2d4edb62338 100644 --- a/.github/workflows/deploy-network.yml +++ b/.github/workflows/deploy-network.yml @@ -228,7 +228,7 @@ jobs: update-irm: needs: deploy-network - if: contains(inputs.network, 'testnet') && !contains(inputs.semver, '-') + if: contains(inputs.network, 'testnet') uses: ./.github/workflows/deploy-irm.yml secrets: inherit with: diff --git a/barretenberg/bbup/bbup b/barretenberg/bbup/bbup index 29d81be28f41..e50950c8e734 100755 --- a/barretenberg/bbup/bbup +++ b/barretenberg/bbup/bbup @@ -114,15 +114,19 @@ install_bb() { local temp_dir=$(mktemp -d) local temp_tar="${temp_dir}/temp.tar.gz" - # Download and extract - local release_url_base="https://github.com/AztecProtocol/aztec-packages/releases/download" + # Download and extract — try AztecProtocol/barretenberg first, fall back to aztec-packages local release_tag="v${version}" if ! version_gte "$version" "0.77.0"; then release_tag="aztec-packages-v${version}" fi - local binary_url="${release_url_base}/${release_tag}/barretenberg-${architecture}-${platform}.tar.gz" + local artifact="barretenberg-${architecture}-${platform}.tar.gz" + local primary_url="https://github.com/AztecProtocol/barretenberg/releases/download/${release_tag}/${artifact}" + local fallback_url="https://github.com/AztecProtocol/aztec-packages/releases/download/${release_tag}/${artifact}" - curl -L --fail "$binary_url" -o "$temp_tar" + if ! curl -sL --fail "$primary_url" -o "$temp_tar"; then + printf "${BLUE}Trying fallback URL...${NC}\n" + curl -L --fail "$fallback_url" -o "$temp_tar" + fi mkdir -p "$BB_PATH" tar xzf "$temp_tar" -C "$BB_PATH" rm -rf "$temp_dir" diff --git a/barretenberg/cpp/bootstrap.sh b/barretenberg/cpp/bootstrap.sh index 2b8b9b031541..98d6f120c560 100755 --- a/barretenberg/cpp/bootstrap.sh +++ b/barretenberg/cpp/bootstrap.sh @@ -323,10 +323,10 @@ function bench { bench_cmds | STRICT_SCHEDULING=1 parallelize } -# Upload assets to release. +# Upload assets to release in AztecProtocol/barretenberg. function release { echo_header "bb cpp release" - do_or_dryrun gh release upload $REF_NAME build-release/* --clobber + do_or_dryrun gh release upload $REF_NAME build-release/* --repo AztecProtocol/barretenberg --clobber } function bench_ivc { diff --git a/barretenberg/rust/barretenberg-rs/README.md b/barretenberg/rust/barretenberg-rs/README.md index 5b182c7ec465..167dd47f2bea 100644 --- a/barretenberg/rust/barretenberg-rs/README.md +++ b/barretenberg/rust/barretenberg-rs/README.md @@ -25,7 +25,7 @@ barretenberg-rs = "0.1" ### PipeBackend (default) -Requires the `bb` binary to be available. Download from [Barretenberg releases](https://github.com/AztecProtocol/aztec-packages/releases). +Requires the `bb` binary to be available. Download from [Barretenberg releases](https://github.com/AztecProtocol/barretenberg/releases). ```rust use barretenberg_rs::{BarretenbergApi, backends::PipeBackend}; diff --git a/barretenberg/rust/barretenberg-rs/build.rs b/barretenberg/rust/barretenberg-rs/build.rs index 3afeaee020d9..db55b653cd11 100644 --- a/barretenberg/rust/barretenberg-rs/build.rs +++ b/barretenberg/rust/barretenberg-rs/build.rs @@ -85,7 +85,7 @@ fn download_lib(out_dir: &PathBuf) { } let url = format!( - "https://github.com/AztecProtocol/aztec-packages/releases/download/v{}/barretenberg-static-{}.tar.gz", + "https://github.com/AztecProtocol/barretenberg/releases/download/v{}/barretenberg-static-{}.tar.gz", version, arch ); diff --git a/barretenberg/rust/bootstrap.sh b/barretenberg/rust/bootstrap.sh index aff9c768d7cf..6c3938fc2d58 100755 --- a/barretenberg/rust/bootstrap.sh +++ b/barretenberg/rust/bootstrap.sh @@ -68,7 +68,7 @@ function release { # Publish to crates.io (--allow-dirty because version was just set and generated files are gitignored) local extra_flags="" - if ! gh release view "v$version" --repo AztecProtocol/aztec-packages &>/dev/null; then + if ! gh release view "v$version" --repo AztecProtocol/barretenberg &>/dev/null; then # No matching GitHub release yet — skip verification build (which would try to download libbb-external.a) echo "No GitHub release found for v$version, adding --no-verify (pass REF_NAME matching a release for full verification)" extra_flags="--no-verify" @@ -96,7 +96,7 @@ function test_download { cargo clean -p barretenberg-rs 2>/dev/null || true # Build with a known release version - local version=${BARRETENBERG_VERSION:-$(gh release list --repo AztecProtocol/aztec-packages --limit 1 --json tagName --jq '.[0].tagName' | sed 's/^v//')} + local version=${BARRETENBERG_VERSION:-$(gh release list --repo AztecProtocol/barretenberg --limit 1 --json tagName --jq '.[0].tagName' | sed 's/^v//')} echo "Testing download with version: $version" # Retry logic for network flakiness (GitHub releases can be flaky) diff --git a/bootstrap.sh b/bootstrap.sh index 8bbd30ce9839..81cc1b33155a 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -486,36 +486,22 @@ function versions { echo "wasi-sdk: $wasi_sdk_version" } -function release_github { - # Add an easy link for comparing to previous release. - local compare_link="" - if gh release view "v$CURRENT_VERSION" &>/dev/null; then - compare_link=$(echo -e "See changes: https://github.com/AztecProtocol/aztec-packages/compare/v${CURRENT_VERSION}...${COMMIT_HASH}") - fi - # Legacy releases. TODO: Eventually remove. - if gh release view "aztec-packages-v$CURRENT_VERSION" &>/dev/null; then - compare_link=$(echo -e "See changes: https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v${CURRENT_VERSION}...${COMMIT_HASH}") +function release_bb_github { + # Create a GitHub release in AztecProtocol/barretenberg for bb artifacts. + # Users can manually create releases in aztec-packages via the GitHub UI if needed. + local bb_repo="AztecProtocol/barretenberg" + if gh release view "$REF_NAME" --repo "$bb_repo" &>/dev/null; then + return fi - # Determine if this is a prerelease (has a prerelease tag like -rc.1, -alpha, etc.) - local is_prerelease=false + local prerelease_flag="" if [ -n "$(semver prerelease $REF_NAME)" ]; then - is_prerelease=true - fi - # Ensure we have a commit release. - if ! gh release view "$REF_NAME" &>/dev/null; then - local prerelease_flag="" - if $is_prerelease; then - prerelease_flag="--prerelease" - fi - do_or_dryrun gh release create "$REF_NAME" \ - $prerelease_flag \ - --target $COMMIT_HASH \ - --title "$REF_NAME" \ - --notes "$compare_link" - elif ! $is_prerelease; then - # Release exists but this is not a prerelease version - ensure it's marked as a full release - do_or_dryrun gh release edit "$REF_NAME" --prerelease=false + prerelease_flag="--prerelease" fi + do_or_dryrun gh release create "$REF_NAME" \ + --repo "$bb_repo" \ + $prerelease_flag \ + --title "$REF_NAME" \ + --notes "Release $REF_NAME — see https://github.com/AztecProtocol/aztec-packages/commits/$COMMIT_HASH" } function release { @@ -525,9 +511,9 @@ function release { echo_header "release all" set -x - # Ensure we have a github release for our REF_NAME. - # This is in case were are not going through release-please. - release_github + # Ensure we have a github release in AztecProtocol/barretenberg for bb artifacts. + # Users can create aztec-packages releases manually via the GitHub "Create a release" button. + release_bb_github projects=( barretenberg/cpp @@ -817,8 +803,24 @@ case "$cmd" in if ! semver check $REF_NAME; then exit 1 fi - build release - release + if [[ "$(semver prerelease $REF_NAME)" == private* ]]; then + echo_header "Private fork release: $REF_NAME" + echo "Creating GitHub release from public repo context (COMMIT_HASH=$COMMIT_HASH)..." + release_github + echo "Fetching private source from aztec-packages-private..." + git remote add private "https://x-access-token:${GITHUB_TOKEN}@github.com/AztecProtocol/aztec-packages-private.git" + git fetch --depth 1 private "refs/tags/$REF_NAME" + git worktree add aztec-private FETCH_HEAD + cd aztec-private + echo "Initializing submodules in private worktree..." + git submodule update --init --recursive + echo "Private worktree ready at $(pwd) (HEAD=$(git rev-parse --short HEAD)). Cache uploads disabled." + export NO_CACHE_UPLOAD=1 + # Unset so child bootstrap.sh re-derives these from the worktree. + unset COMMIT_HASH root + fi + ./bootstrap.sh build release + ./bootstrap.sh release ;; ########################## diff --git a/ci3/cache_upload b/ci3/cache_upload index 213fe4551f4b..21a206d7c934 100755 --- a/ci3/cache_upload +++ b/ci3/cache_upload @@ -22,6 +22,11 @@ if [[ -z "${S3_FORCE_UPLOAD:-}" && "${CI:-0}" -eq 0 ]]; then exit 0 fi +if [[ "${NO_CACHE_UPLOAD:-0}" -eq 1 ]]; then + echo_stderr "Skipping upload because NO_CACHE_UPLOAD=1." + exit 0 +fi + # In SSM/instance-profile mode, AWS CLI falls back to IMDS for credentials. if [[ "${CI_SSM_MODE:-0}" -eq 0 ]]; then if [[ -z "${AWS_ACCESS_KEY_ID:-}" || -z "${AWS_SECRET_ACCESS_KEY:-}" ]] && ! aws configure get aws_access_key_id &>/dev/null; then diff --git a/ci3/parallelize_strict b/ci3/parallelize_strict index 95e244490fa4..7683638abd8f 100755 --- a/ci3/parallelize_strict +++ b/ci3/parallelize_strict @@ -75,13 +75,18 @@ num_jobs=0 # We'll handle errors explicitly. set +e -run_tests | tee $output +set -o pipefail +run_tests | tee "$output" +run_exit=$? # Loop to monitor jobs until one fails or all finish. while (( $(jobs -p | wc -l) > 0 )); do wait_for_job done +# Propagate failure from run_tests (which runs in a subshell due to the pipe). +[ $run_exit -ne 0 ] && exit $run_exit + function filter_long_times { grep -E '\([0-9]+s\)$' | # Match lines ending with (number)s sed 's/.*(\([0-9]\+\)s)$/\1 &/' | # Extract number and keep original line diff --git a/spartan/aztec-bot/templates/env.configmap.yaml b/spartan/aztec-bot/templates/env.configmap.yaml index a741eacd919c..92780250772a 100644 --- a/spartan/aztec-bot/templates/env.configmap.yaml +++ b/spartan/aztec-bot/templates/env.configmap.yaml @@ -17,3 +17,9 @@ data: BOT_STOP_WHEN_UNHEALTHY: {{ .Values.bot.stopIfUnhealthy | quote }} AZTEC_NODE_URL: {{ .Values.bot.nodeUrl | quote }} TEST_ACCOUNTS: {{ .Values.bot.testAccounts | quote }} + {{- if .Values.bot.daGasLimit }} + BOT_DA_GAS_LIMIT: {{ .Values.bot.daGasLimit | quote }} + {{- end }} + {{- if .Values.bot.l2GasLimit }} + BOT_L2_GAS_LIMIT: {{ .Values.bot.l2GasLimit | quote }} + {{- end }} diff --git a/spartan/aztec-bot/values.yaml b/spartan/aztec-bot/values.yaml index 5c55acc21846..57e2f2d1ce9a 100644 --- a/spartan/aztec-bot/values.yaml +++ b/spartan/aztec-bot/values.yaml @@ -21,6 +21,8 @@ bot: nodeUrl: "" testAccounts: false botPrivateKey: "0xcafe" + daGasLimit: "" + l2GasLimit: "" persistence: enabled: false diff --git a/spartan/environments/staging-public.env b/spartan/environments/staging-public.env index 73886cbc574a..fa67490c7688 100644 --- a/spartan/environments/staging-public.env +++ b/spartan/environments/staging-public.env @@ -49,6 +49,9 @@ PROVER_REPLICAS=4 PUBLISHERS_PER_PROVER=2 PROVER_PUBLISHER_MNEMONIC_START_INDEX=8000 +BOT_DA_GAS_LIMIT=100000 +BOT_L2_GAS_LIMIT=6540000 + BOT_TRANSFERS_REPLICAS=1 BOT_TRANSFERS_TX_INTERVAL_SECONDS=250 BOT_TRANSFERS_FOLLOW_CHAIN=PROPOSED diff --git a/spartan/terraform/deploy-aztec-infra/main.tf b/spartan/terraform/deploy-aztec-infra/main.tf index d1fc361a0ea3..3bcdd28ca4a5 100644 --- a/spartan/terraform/deploy-aztec-infra/main.tf +++ b/spartan/terraform/deploy-aztec-infra/main.tf @@ -224,6 +224,8 @@ locals { "validator.node.env.BLOB_ALLOW_EMPTY_SOURCES" = var.BLOB_ALLOW_EMPTY_SOURCES "validator.node.env.P2P_MAX_TX_POOL_SIZE" = var.P2P_MAX_TX_POOL_SIZE "validator.node.env.PROVER_TEST_VERIFICATION_DELAY_MS" = var.PROVER_TEST_VERIFICATION_DELAY_MS + "validator.node.env.BB_CHONK_VERIFY_MAX_BATCH" = var.BB_CHONK_VERIFY_MAX_BATCH + "validator.node.env.BB_CHONK_VERIFY_BATCH_CONCURRENCY" = var.BB_CHONK_VERIFY_BATCH_CONCURRENCY "validator.node.env.DEBUG_P2P_INSTRUMENT_MESSAGES" = var.DEBUG_P2P_INSTRUMENT_MESSAGES "validator.node.secret.envEnabled" = true "validator.node.secret.mnemonic" = var.VALIDATOR_MNEMONIC @@ -379,6 +381,8 @@ locals { "node.node.env.L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE" = var.PROVER_L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE "node.node.env.P2P_MAX_TX_POOL_SIZE" = var.P2P_MAX_TX_POOL_SIZE "node.node.env.PROVER_TEST_VERIFICATION_DELAY_MS" = var.PROVER_TEST_VERIFICATION_DELAY_MS + "node.node.env.BB_CHONK_VERIFY_MAX_BATCH" = var.BB_CHONK_VERIFY_MAX_BATCH + "node.node.env.BB_CHONK_VERIFY_BATCH_CONCURRENCY" = var.BB_CHONK_VERIFY_BATCH_CONCURRENCY "node.node.env.DEBUG_P2P_INSTRUMENT_MESSAGES" = var.DEBUG_P2P_INSTRUMENT_MESSAGES "node.node.env.P2P_GOSSIPSUB_D" = var.P2P_GOSSIPSUB_D "node.node.env.P2P_GOSSIPSUB_DLO" = var.P2P_GOSSIPSUB_DLO @@ -461,6 +465,8 @@ locals { "node.env.BLOB_ALLOW_EMPTY_SOURCES" = var.BLOB_ALLOW_EMPTY_SOURCES "node.env.P2P_MAX_TX_POOL_SIZE" = var.P2P_MAX_TX_POOL_SIZE "node.env.PROVER_TEST_VERIFICATION_DELAY_MS" = var.PROVER_TEST_VERIFICATION_DELAY_MS + "node.env.BB_CHONK_VERIFY_MAX_BATCH" = var.BB_CHONK_VERIFY_MAX_BATCH + "node.env.BB_CHONK_VERIFY_BATCH_CONCURRENCY" = var.BB_CHONK_VERIFY_BATCH_CONCURRENCY "node.env.DEBUG_P2P_INSTRUMENT_MESSAGES" = var.DEBUG_P2P_INSTRUMENT_MESSAGES "node.env.P2P_GOSSIPSUB_D" = var.P2P_GOSSIPSUB_D "node.env.P2P_GOSSIPSUB_DLO" = var.P2P_GOSSIPSUB_DLO @@ -515,6 +521,8 @@ locals { "node.env.BLOB_ALLOW_EMPTY_SOURCES" = var.BLOB_ALLOW_EMPTY_SOURCES "node.env.P2P_MAX_TX_POOL_SIZE" = var.P2P_MAX_TX_POOL_SIZE "node.env.PROVER_TEST_VERIFICATION_DELAY_MS" = var.PROVER_TEST_VERIFICATION_DELAY_MS + "node.env.BB_CHONK_VERIFY_MAX_BATCH" = var.BB_CHONK_VERIFY_MAX_BATCH + "node.env.BB_CHONK_VERIFY_BATCH_CONCURRENCY" = var.BB_CHONK_VERIFY_BATCH_CONCURRENCY "node.env.DEBUG_P2P_INSTRUMENT_MESSAGES" = var.DEBUG_P2P_INSTRUMENT_MESSAGES "node.otelIncludeMetrics" = var.FULL_NODE_INCLUDE_METRICS "node.env.P2P_GOSSIPSUB_D" = var.P2P_GOSSIPSUB_D @@ -551,6 +559,8 @@ locals { "node.env.P2P_ARCHIVED_TX_LIMIT" = "10000000" "node.proverRealProofs" = var.PROVER_REAL_PROOFS "node.env.PROVER_TEST_VERIFICATION_DELAY_MS" = var.PROVER_TEST_VERIFICATION_DELAY_MS + "node.env.BB_CHONK_VERIFY_MAX_BATCH" = var.BB_CHONK_VERIFY_MAX_BATCH + "node.env.BB_CHONK_VERIFY_BATCH_CONCURRENCY" = var.BB_CHONK_VERIFY_BATCH_CONCURRENCY "node.env.DEBUG_FORCE_TX_PROOF_VERIFICATION" = var.DEBUG_FORCE_TX_PROOF_VERIFICATION "node.env.DEBUG_P2P_INSTRUMENT_MESSAGES" = var.DEBUG_P2P_INSTRUMENT_MESSAGES "node.env.P2P_TX_POOL_DELETE_TXS_AFTER_REORG" = var.P2P_TX_POOL_DELETE_TXS_AFTER_REORG @@ -621,6 +631,8 @@ locals { "bot.nodeUrl" = local.internal_rpc_url "bot.mnemonic" = var.BOT_MNEMONIC "bot.mnemonicStartIndex" = var.BOT_TRANSFERS_MNEMONIC_START_INDEX + "bot.daGasLimit" = var.BOT_DA_GAS_LIMIT + "bot.l2GasLimit" = var.BOT_L2_GAS_LIMIT } boot_node_host_path = "" bootstrap_nodes_path = "" @@ -645,6 +657,8 @@ locals { "bot.nodeUrl" = local.internal_rpc_url "bot.mnemonic" = var.BOT_MNEMONIC "bot.mnemonicStartIndex" = var.BOT_SWAPS_MNEMONIC_START_INDEX + "bot.daGasLimit" = var.BOT_DA_GAS_LIMIT + "bot.l2GasLimit" = var.BOT_L2_GAS_LIMIT } boot_node_host_path = "" bootstrap_nodes_path = "" @@ -669,6 +683,8 @@ locals { "bot.nodeUrl" = local.internal_rpc_url "bot.mnemonic" = var.BOT_MNEMONIC "bot.mnemonicStartIndex" = var.BOT_CROSS_CHAIN_MNEMONIC_START_INDEX + "bot.daGasLimit" = var.BOT_DA_GAS_LIMIT + "bot.l2GasLimit" = var.BOT_L2_GAS_LIMIT } boot_node_host_path = "" bootstrap_nodes_path = "" diff --git a/spartan/terraform/deploy-aztec-infra/variables.tf b/spartan/terraform/deploy-aztec-infra/variables.tf index 67165fde6035..efae777c52fb 100644 --- a/spartan/terraform/deploy-aztec-infra/variables.tf +++ b/spartan/terraform/deploy-aztec-infra/variables.tf @@ -81,6 +81,18 @@ variable "PROVER_TEST_VERIFICATION_DELAY_MS" { default = 10 } +variable "BB_CHONK_VERIFY_MAX_BATCH" { + description = "Upper bound on proofs per batch for the peer chonk batch verifier" + type = number + default = 16 +} + +variable "BB_CHONK_VERIFY_BATCH_CONCURRENCY" { + description = "Thread count for the peer batch verifier parallel reduce (0 = auto)" + type = number + default = 6 +} + variable "K8S_CLUSTER_CONTEXT" { description = "GKE cluster context" type = string @@ -649,6 +661,18 @@ variable "BOT_CROSS_CHAIN_PXE_SYNC_CHAIN_TIP" { default = "checkpointed" } +variable "BOT_DA_GAS_LIMIT" { + description = "DA gas limit for bot transactions (empty to use gas estimation)" + type = string + default = "" +} + +variable "BOT_L2_GAS_LIMIT" { + description = "L2 gas limit for bot transactions (empty to use gas estimation)" + type = string + default = "" +} + # RPC ingress configuration (GKE-specific) variable "RPC_INGRESS_ENABLED" { description = "Enable GKE ingress for RPC nodes" diff --git a/yarn-project/.claude/rules/typescript-style.md b/yarn-project/.claude/rules/typescript-style.md index 5ea500e2e736..0990cc1f0c00 100644 --- a/yarn-project/.claude/rules/typescript-style.md +++ b/yarn-project/.claude/rules/typescript-style.md @@ -330,4 +330,20 @@ mock.getData.mockImplementation((id: string) => { } return Promise.resolve(undefined); }); -``` \ No newline at end of file +``` + +## Arrow Function Bodies + +Use expression bodies instead of block bodies when the block only contains a `return`: + +```typescript +// Good: Expression body +items.map(item => item.value * 2) +fn(arg => expression(arg, foo)) + +// Bad: Block body with just a return +items.map(item => { return item.value * 2; }) +fn(arg => { return expression(arg, foo); }) +``` + +Block bodies are appropriate when the callback has multiple statements or side effects beyond the return. \ No newline at end of file diff --git a/yarn-project/archiver/src/archiver.ts b/yarn-project/archiver/src/archiver.ts index b34c42661457..f9e7efa31461 100644 --- a/yarn-project/archiver/src/archiver.ts +++ b/yarn-project/archiver/src/archiver.ts @@ -341,19 +341,33 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra return Promise.resolve(this.synchronizer.getL1Timestamp()); } - public getSyncedL2SlotNumber(): Promise { + public async getSyncedL2SlotNumber(): Promise { + // The synced L2 slot is the latest slot for which we have all L1 data, + // either because we have seen all L1 blocks for that slot, or because + // we have seen the corresponding checkpoint. + + let slotFromL1Sync: SlotNumber | undefined; const l1Timestamp = this.synchronizer.getL1Timestamp(); - if (l1Timestamp === undefined) { - return Promise.resolve(undefined); + if (l1Timestamp !== undefined) { + const nextL1BlockSlot = getSlotAtNextL1Block(l1Timestamp, this.l1Constants); + if (Number(nextL1BlockSlot) > 0) { + slotFromL1Sync = SlotNumber.add(nextL1BlockSlot, -1); + } + } + + let slotFromCheckpoint: SlotNumber | undefined; + const latestCheckpointNumber = await this.store.getSynchedCheckpointNumber(); + if (latestCheckpointNumber > 0) { + const checkpointData = await this.store.getCheckpointData(latestCheckpointNumber); + if (checkpointData) { + slotFromCheckpoint = checkpointData.header.slotNumber; + } } - // The synced slot is the last L2 slot whose all L1 blocks have been processed. - // If the next L1 block (at l1Timestamp + ethereumSlotDuration) falls in slot N, - // then we've fully synced slot N-1. - const nextL1BlockSlot = getSlotAtNextL1Block(l1Timestamp, this.l1Constants); - if (Number(nextL1BlockSlot) === 0) { - return Promise.resolve(undefined); + + if (slotFromL1Sync === undefined && slotFromCheckpoint === undefined) { + return undefined; } - return Promise.resolve(SlotNumber(nextL1BlockSlot - 1)); + return SlotNumber(Math.max(slotFromL1Sync ?? 0, slotFromCheckpoint ?? 0)); } public async getSyncedL2EpochNumber(): Promise { diff --git a/yarn-project/archiver/src/factory.ts b/yarn-project/archiver/src/factory.ts index 5aa14b3ca7a0..db7b06b5b1da 100644 --- a/yarn-project/archiver/src/factory.ts +++ b/yarn-project/archiver/src/factory.ts @@ -13,7 +13,7 @@ import { protocolContractNames } from '@aztec/protocol-contracts'; import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle'; import { FunctionType, decodeFunctionSignature } from '@aztec/stdlib/abi'; import type { ArchiverEmitter } from '@aztec/stdlib/block'; -import { type ContractClassPublic, computePublicBytecodeCommitment } from '@aztec/stdlib/contract'; +import { type ContractClassPublicWithCommitment, computePublicBytecodeCommitment } from '@aztec/stdlib/contract'; import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { getTelemetryClient } from '@aztec/telemetry-client'; @@ -173,16 +173,22 @@ export async function createArchiver( return archiver; } -/** Registers protocol contracts in the archiver store. */ +/** Registers protocol contracts in the archiver store. Idempotent — skips contracts that already exist (e.g. on node restart). */ export async function registerProtocolContracts(store: KVArchiverDataStore) { const blockNumber = 0; for (const name of protocolContractNames) { const provider = new BundledProtocolContractsProvider(); const contract = await provider.getProtocolContractArtifact(name); - const contractClassPublic: ContractClassPublic = { + + // Skip if already registered (happens on node restart with a persisted store). + if (await store.getContractClass(contract.contractClass.id)) { + continue; + } + + const publicBytecodeCommitment = await computePublicBytecodeCommitment(contract.contractClass.packedBytecode); + const contractClassPublic: ContractClassPublicWithCommitment = { ...contract.contractClass, - privateFunctions: [], - utilityFunctions: [], + publicBytecodeCommitment, }; const publicFunctionSignatures = contract.artifact.functions @@ -190,8 +196,7 @@ export async function registerProtocolContracts(store: KVArchiverDataStore) { .map(fn => decodeFunctionSignature(fn.name, fn.parameters)); await store.registerContractFunctionSignatures(publicFunctionSignatures); - const bytecodeCommitment = await computePublicBytecodeCommitment(contractClassPublic.packedBytecode); - await store.addContractClasses([contractClassPublic], [bytecodeCommitment], BlockNumber(blockNumber)); + await store.addContractClasses([contractClassPublic], BlockNumber(blockNumber)); await store.addContractInstances([contract.instance], BlockNumber(blockNumber)); } } diff --git a/yarn-project/archiver/src/modules/data_store_updater.ts b/yarn-project/archiver/src/modules/data_store_updater.ts index 32087d4e2b7a..cd165b562e28 100644 --- a/yarn-project/archiver/src/modules/data_store_updater.ts +++ b/yarn-project/archiver/src/modules/data_store_updater.ts @@ -1,11 +1,7 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; -import { Fr } from '@aztec/foundation/curves/bn254'; +import { filterAsync } from '@aztec/foundation/collection'; import { createLogger } from '@aztec/foundation/log'; -import { - ContractClassPublishedEvent, - PrivateFunctionBroadcastedEvent, - UtilityFunctionBroadcastedEvent, -} from '@aztec/protocol-contracts/class-registry'; +import { ContractClassPublishedEvent } from '@aztec/protocol-contracts/class-registry'; import { ContractInstancePublishedEvent, ContractInstanceUpdatedEvent, @@ -13,17 +9,13 @@ import { import type { L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block'; import { type PublishedCheckpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint'; import { - type ExecutablePrivateFunctionWithMembershipProof, - type UtilityFunctionWithMembershipProof, - computePublicBytecodeCommitment, - isValidPrivateFunctionMembershipProof, - isValidUtilityFunctionMembershipProof, + type ContractClassPublicWithCommitment, + computeContractAddressFromInstance, + computeContractClassId, } from '@aztec/stdlib/contract'; import type { ContractClassLog, PrivateLog, PublicLog } from '@aztec/stdlib/logs'; import type { UInt64 } from '@aztec/stdlib/types'; -import groupBy from 'lodash.groupby'; - import type { KVArchiverDataStore } from '../store/kv_archiver_store.js'; import type { L2TipsCache } from '../store/l2_tips_cache.js'; @@ -54,8 +46,7 @@ export class ArchiverDataStoreUpdater { /** * Adds a proposed block to the store with contract class/instance extraction from logs. * This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1. - * Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events, - * and individually broadcasted functions from the block logs. + * Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events from the block logs. * * @param block - The proposed L2 block to add. * @param pendingChainValidationStatus - Optional validation status to set. @@ -87,8 +78,7 @@ export class ArchiverDataStoreUpdater { * Reconciles local blocks with incoming checkpoints from L1. * Adds new checkpoints to the store with contract class/instance extraction from logs. * Prunes any local blocks that conflict with checkpoint data (by comparing archive roots). - * Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events, - * and individually broadcasted functions from the checkpoint block logs. + * Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events from the checkpoint block logs. * * @param checkpoints - The published checkpoints to add. * @param pendingChainValidationStatus - Optional validation status to set. @@ -313,9 +303,6 @@ export class ArchiverDataStoreUpdater { this.updatePublishedContractClasses(contractClassLogs, block.number, operation), this.updateDeployedContractInstances(privateLogs, block.number, operation), this.updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, operation), - operation === Operation.Store - ? this.storeBroadcastedIndividualFunctions(contractClassLogs, block.number) - : Promise.resolve(true), ]) ).every(Boolean); } @@ -332,18 +319,37 @@ export class ArchiverDataStoreUpdater { .filter(log => ContractClassPublishedEvent.isContractClassPublishedEvent(log)) .map(log => ContractClassPublishedEvent.fromLog(log)); - const contractClasses = await Promise.all(contractClassPublishedEvents.map(e => e.toContractClassPublic())); - if (contractClasses.length > 0) { - contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`)); - if (operation == Operation.Store) { - // TODO: Will probably want to create some worker threads to compute these bytecode commitments as they are expensive - const commitments = await Promise.all( - contractClasses.map(c => computePublicBytecodeCommitment(c.packedBytecode)), - ); - return await this.store.addContractClasses(contractClasses, commitments, blockNum); - } else if (operation == Operation.Delete) { + if (operation == Operation.Delete) { + const contractClasses = contractClassPublishedEvents.map(e => e.toContractClassPublic()); + if (contractClasses.length > 0) { + contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`)); return await this.store.deleteContractClasses(contractClasses, blockNum); } + return true; + } + + // Compute bytecode commitments and validate class IDs in a single pass. + const contractClasses: ContractClassPublicWithCommitment[] = []; + for (const event of contractClassPublishedEvents) { + const contractClass = await event.toContractClassPublicWithBytecodeCommitment(); + const computedClassId = await computeContractClassId({ + artifactHash: contractClass.artifactHash, + privateFunctionsRoot: contractClass.privateFunctionsRoot, + publicBytecodeCommitment: contractClass.publicBytecodeCommitment, + }); + if (!computedClassId.equals(contractClass.id)) { + this.log.warn( + `Skipping contract class with mismatched id at block ${blockNum}. Claimed ${contractClass.id}, computed ${computedClassId}`, + { blockNum, contractClassId: event.contractClassId.toString() }, + ); + continue; + } + contractClasses.push(contractClass); + } + + if (contractClasses.length > 0) { + contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`)); + return await this.store.addContractClasses(contractClasses, blockNum); } return true; } @@ -356,10 +362,27 @@ export class ArchiverDataStoreUpdater { blockNum: BlockNumber, operation: Operation, ): Promise { - const contractInstances = allLogs + const allInstances = allLogs .filter(log => ContractInstancePublishedEvent.isContractInstancePublishedEvent(log)) .map(log => ContractInstancePublishedEvent.fromLog(log)) .map(e => e.toContractInstance()); + + // Verify that each instance's address matches the one derived from its fields if we're adding + const contractInstances = + operation === Operation.Delete + ? allInstances + : await filterAsync(allInstances, async instance => { + const computedAddress = await computeContractAddressFromInstance(instance); + if (!computedAddress.equals(instance.address)) { + this.log.warn( + `Found contract instance with mismatched address at block ${blockNum}. Claimed ${instance.address} but computed ${computedAddress}.`, + { instanceAddress: instance.address.toString(), computedAddress: computedAddress.toString(), blockNum }, + ); + return false; + } + return true; + }); + if (contractInstances.length > 0) { contractInstances.forEach(c => this.log.verbose(`${Operation[operation]} contract instance at ${c.address.toString()}`), @@ -398,67 +421,4 @@ export class ArchiverDataStoreUpdater { } return true; } - - /** - * Stores the functions that were broadcasted individually. - * - * @dev Beware that there is not a delete variant of this, since they are added to contract classes - * and will be deleted as part of the class if needed. - */ - private async storeBroadcastedIndividualFunctions( - allLogs: ContractClassLog[], - _blockNum: BlockNumber, - ): Promise { - // Filter out private and utility function broadcast events - const privateFnEvents = allLogs - .filter(log => PrivateFunctionBroadcastedEvent.isPrivateFunctionBroadcastedEvent(log)) - .map(log => PrivateFunctionBroadcastedEvent.fromLog(log)); - const utilityFnEvents = allLogs - .filter(log => UtilityFunctionBroadcastedEvent.isUtilityFunctionBroadcastedEvent(log)) - .map(log => UtilityFunctionBroadcastedEvent.fromLog(log)); - - // Group all events by contract class id - for (const [classIdString, classEvents] of Object.entries( - groupBy([...privateFnEvents, ...utilityFnEvents], e => e.contractClassId.toString()), - )) { - const contractClassId = Fr.fromHexString(classIdString); - const contractClass = await this.store.getContractClass(contractClassId); - if (!contractClass) { - this.log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`); - continue; - } - - // Split private and utility functions, and filter out invalid ones - const allFns = classEvents.map(e => e.toFunctionWithMembershipProof()); - const privateFns = allFns.filter( - (fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'utilityFunctionsTreeRoot' in fn, - ); - const utilityFns = allFns.filter( - (fn): fn is UtilityFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn, - ); - - const privateFunctionsWithValidity = await Promise.all( - privateFns.map(async fn => ({ fn, valid: await isValidPrivateFunctionMembershipProof(fn, contractClass) })), - ); - const validPrivateFns = privateFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn); - const utilityFunctionsWithValidity = await Promise.all( - utilityFns.map(async fn => ({ - fn, - valid: await isValidUtilityFunctionMembershipProof(fn, contractClass), - })), - ); - const validUtilityFns = utilityFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn); - const validFnCount = validPrivateFns.length + validUtilityFns.length; - if (validFnCount !== allFns.length) { - this.log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`); - } - - // Store the functions in the contract class in a single operation - if (validFnCount > 0) { - this.log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`); - } - await this.store.addFunctions(contractClassId, validPrivateFns, validUtilityFns); - } - return true; - } } diff --git a/yarn-project/archiver/src/store/contract_class_store.ts b/yarn-project/archiver/src/store/contract_class_store.ts index 36de477aad42..bf266c3a086a 100644 --- a/yarn-project/archiver/src/store/contract_class_store.ts +++ b/yarn-project/archiver/src/store/contract_class_store.ts @@ -2,14 +2,7 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import { toArray } from '@aztec/foundation/iterable'; import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize'; import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store'; -import { FunctionSelector } from '@aztec/stdlib/abi'; -import type { - ContractClassPublic, - ContractClassPublicWithBlockNumber, - ExecutablePrivateFunctionWithMembershipProof, - UtilityFunctionWithMembershipProof, -} from '@aztec/stdlib/contract'; -import { Vector } from '@aztec/stdlib/types'; +import type { ContractClassPublic, ContractClassPublicWithBlockNumber } from '@aztec/stdlib/contract'; /** * LMDB-based contract class storage for the archiver. @@ -29,11 +22,15 @@ export class ContractClassStore { blockNumber: number, ): Promise { await this.db.transactionAsync(async () => { - await this.#contractClasses.setIfNotExists( - contractClass.id.toString(), + const key = contractClass.id.toString(); + if (await this.#contractClasses.hasAsync(key)) { + throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`); + } + await this.#contractClasses.set( + key, serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }), ); - await this.#bytecodeCommitments.setIfNotExists(contractClass.id.toString(), bytecodeCommitment.toBuffer()); + await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer()); }); } @@ -60,37 +57,6 @@ export class ContractClassStore { async getContractClassIds(): Promise { return (await toArray(this.#contractClasses.keysAsync())).map(key => Fr.fromHexString(key)); } - - async addFunctions( - contractClassId: Fr, - newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[], - newUtilityFunctions: UtilityFunctionWithMembershipProof[], - ): Promise { - await this.db.transactionAsync(async () => { - const existingClassBuffer = await this.#contractClasses.getAsync(contractClassId.toString()); - if (!existingClassBuffer) { - throw new Error(`Unknown contract class ${contractClassId} when adding private functions to store`); - } - - const existingClass = deserializeContractClassPublic(existingClassBuffer); - const { privateFunctions: existingPrivateFns, utilityFunctions: existingUtilityFns } = existingClass; - - const updatedClass: Omit = { - ...existingClass, - privateFunctions: [ - ...existingPrivateFns, - ...newPrivateFunctions.filter(newFn => !existingPrivateFns.some(f => f.selector.equals(newFn.selector))), - ], - utilityFunctions: [ - ...existingUtilityFns, - ...newUtilityFunctions.filter(newFn => !existingUtilityFns.some(f => f.selector.equals(newFn.selector))), - ], - }; - await this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass)); - }); - - return true; - } } function serializeContractClassPublic(contractClass: Omit): Buffer { @@ -98,83 +64,19 @@ function serializeContractClassPublic(contractClass: Omit { const reader = BufferReader.asReader(buffer); return { l2BlockNumber: reader.readNumber(), version: reader.readUInt8() as 1, artifactHash: reader.readObject(Fr), - privateFunctions: reader.readVector({ fromBuffer: deserializePrivateFunction }), - utilityFunctions: reader.readVector({ fromBuffer: deserializeUtilityFunction }), packedBytecode: reader.readBuffer(), privateFunctionsRoot: reader.readObject(Fr), }; } - -function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePrivateFunctionWithMembershipProof { - const reader = BufferReader.asReader(buffer); - return { - selector: reader.readObject(FunctionSelector), - vkHash: reader.readObject(Fr), - bytecode: reader.readBuffer(), - functionMetadataHash: reader.readObject(Fr), - artifactMetadataHash: reader.readObject(Fr), - utilityFunctionsTreeRoot: reader.readObject(Fr), - privateFunctionTreeSiblingPath: reader.readVector(Fr), - privateFunctionTreeLeafIndex: reader.readNumber(), - artifactTreeSiblingPath: reader.readVector(Fr), - artifactTreeLeafIndex: reader.readNumber(), - }; -} - -function deserializeUtilityFunction(buffer: Buffer | BufferReader): UtilityFunctionWithMembershipProof { - const reader = BufferReader.asReader(buffer); - return { - selector: reader.readObject(FunctionSelector), - bytecode: reader.readBuffer(), - functionMetadataHash: reader.readObject(Fr), - artifactMetadataHash: reader.readObject(Fr), - privateFunctionsArtifactTreeRoot: reader.readObject(Fr), - artifactTreeSiblingPath: reader.readVector(Fr), - artifactTreeLeafIndex: reader.readNumber(), - }; -} diff --git a/yarn-project/archiver/src/store/contract_instance_store.ts b/yarn-project/archiver/src/store/contract_instance_store.ts index 63ea37ff9bcb..332605240e04 100644 --- a/yarn-project/archiver/src/store/contract_instance_store.ts +++ b/yarn-project/archiver/src/store/contract_instance_store.ts @@ -27,11 +27,14 @@ export class ContractInstanceStore { addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise { return this.db.transactionAsync(async () => { - await this.#contractInstances.set( - contractInstance.address.toString(), - new SerializableContractInstance(contractInstance).toBuffer(), - ); - await this.#contractInstancePublishedAt.set(contractInstance.address.toString(), blockNumber); + const key = contractInstance.address.toString(); + if (await this.#contractInstances.hasAsync(key)) { + throw new Error( + `Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`, + ); + } + await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer()); + await this.#contractInstancePublishedAt.set(key, blockNumber); }); } diff --git a/yarn-project/archiver/src/store/kv_archiver_store.test.ts b/yarn-project/archiver/src/store/kv_archiver_store.test.ts index 92ae927f0ef3..19620de355c0 100644 --- a/yarn-project/archiver/src/store/kv_archiver_store.test.ts +++ b/yarn-project/archiver/src/store/kv_archiver_store.test.ts @@ -7,7 +7,6 @@ import { SlotNumber, } from '@aztec/foundation/branded-types'; import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; -import { times } from '@aztec/foundation/collection'; import { randomInt } from '@aztec/foundation/crypto/random'; import { Fr } from '@aztec/foundation/curves/bn254'; import { toArray } from '@aztec/foundation/iterable'; @@ -25,6 +24,7 @@ import { import { Checkpoint, PublishedCheckpoint, randomCheckpointInfo } from '@aztec/stdlib/checkpoint'; import { type ContractClassPublic, + type ContractClassPublicWithCommitment, type ContractInstanceWithAddress, SerializableContractInstance, computePublicBytecodeCommitment, @@ -32,11 +32,7 @@ import { import { MAX_LOGS_PER_TAG } from '@aztec/stdlib/interfaces/api-limit'; import { ContractClassLog, LogId } from '@aztec/stdlib/logs'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; -import { - makeContractClassPublic, - makeExecutablePrivateFunctionWithMembershipProof, - makeUtilityFunctionWithMembershipProof, -} from '@aztec/stdlib/testing'; +import { makeContractClassPublic } from '@aztec/stdlib/testing'; import '@aztec/stdlib/testing/jest'; import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees'; import { type IndexedTxEffect, TxHash } from '@aztec/stdlib/tx'; @@ -80,6 +76,13 @@ async function addProposedBlocks( return result; } +async function withCommitment(contractClass: ContractClassPublic): Promise { + return { + ...contractClass, + publicBytecodeCommitment: await computePublicBytecodeCommitment(contractClass.packedBytecode), + }; +} + describe('KVArchiverDataStore', () => { let store: KVArchiverDataStore; let publishedCheckpoints: PublishedCheckpoint[]; @@ -2351,11 +2354,7 @@ describe('KVArchiverDataStore', () => { beforeEach(async () => { contractClass = await makeContractClassPublic(); - await store.addContractClasses( - [contractClass], - [await computePublicBytecodeCommitment(contractClass.packedBytecode)], - BlockNumber(blockNum), - ); + await store.addContractClasses([await withCommitment(contractClass)], BlockNumber(blockNum)); }); it('returns previously stored contract class', async () => { @@ -2367,12 +2366,13 @@ describe('KVArchiverDataStore', () => { await expect(store.getContractClass(contractClass.id)).resolves.toBeUndefined(); }); - it('returns contract class if later "deployment" class was deleted', async () => { - await store.addContractClasses( - [contractClass], - [await computePublicBytecodeCommitment(contractClass.packedBytecode)], - BlockNumber(blockNum + 1), - ); + it('throws if the same contract class is added again', async () => { + await expect( + store.addContractClasses([await withCommitment(contractClass)], BlockNumber(blockNum + 1)), + ).rejects.toThrow(/already exists/); + }); + + it('returns contract class if deleted at a later block number', async () => { await store.deleteContractClasses([contractClass], BlockNumber(blockNum + 1)); await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass); }); @@ -2380,36 +2380,6 @@ describe('KVArchiverDataStore', () => { it('returns undefined if contract class is not found', async () => { await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined(); }); - - it('adds new private functions', async () => { - const fns = times(3, makeExecutablePrivateFunctionWithMembershipProof); - await store.addFunctions(contractClass.id, fns, []); - const stored = await store.getContractClass(contractClass.id); - expect(stored?.privateFunctions).toEqual(fns); - }); - - it('does not duplicate private functions', async () => { - const fns = times(3, makeExecutablePrivateFunctionWithMembershipProof); - await store.addFunctions(contractClass.id, fns.slice(0, 1), []); - await store.addFunctions(contractClass.id, fns, []); - const stored = await store.getContractClass(contractClass.id); - expect(stored?.privateFunctions).toEqual(fns); - }); - - it('adds new utility functions', async () => { - const fns = times(3, makeUtilityFunctionWithMembershipProof); - await store.addFunctions(contractClass.id, [], fns); - const stored = await store.getContractClass(contractClass.id); - expect(stored?.utilityFunctions).toEqual(fns); - }); - - it('does not duplicate utility functions', async () => { - const fns = times(3, makeUtilityFunctionWithMembershipProof); - await store.addFunctions(contractClass.id, [], fns.slice(0, 1)); - await store.addFunctions(contractClass.id, [], fns); - const stored = await store.getContractClass(contractClass.id); - expect(stored?.utilityFunctions).toEqual(fns); - }); }); // Note that a lot of tests here are basically duplicates of the ones in getPublicLogsByTagsFromContract but @@ -3431,28 +3401,19 @@ describe('KVArchiverDataStore', () => { expect(storedBlock?.archive.root.equals(provisionalBlock.archive.root)).toBe(true); }); - it('does not throw when adding the same contract class twice', async () => { + it('throws when adding the same contract class twice', async () => { const contractClass = await makeContractClassPublic(); - const commitment = await computePublicBytecodeCommitment(contractClass.packedBytecode); - - // Add contract class first time - await store.addContractClasses([contractClass], [commitment], BlockNumber(1)); + const contractClassWithCommitment = await withCommitment(contractClass); - // Add same contract class again - should not throw (uses setIfNotExists) - await store.addContractClasses([contractClass], [commitment], BlockNumber(2)); - - // Verify contract class exists - const retrieved = await store.getContractClass(contractClass.id); - expect(retrieved).toBeDefined(); + await store.addContractClasses([contractClassWithCommitment], BlockNumber(1)); + await expect(store.addContractClasses([contractClassWithCommitment], BlockNumber(2))).rejects.toThrow( + /already exists/, + ); }); - it('does not throw when adding the same contract instance twice', async () => { + it('throws when adding the same contract instance twice', async () => { const contractClass = await makeContractClassPublic(); - await store.addContractClasses( - [contractClass], - [await computePublicBytecodeCommitment(contractClass.packedBytecode)], - BlockNumber(1), - ); + await store.addContractClasses([await withCommitment(contractClass)], BlockNumber(1)); const instance = { ...(await SerializableContractInstance.random({ @@ -3462,16 +3423,8 @@ describe('KVArchiverDataStore', () => { address: await AztecAddress.random(), }; - // Add contract instance first time await store.addContractInstances([instance], BlockNumber(1)); - - // Add same contract instance again - should not throw (uses set) - await store.addContractInstances([instance], BlockNumber(2)); - - // Verify instance exists - const retrieved = await store.getContractInstance(instance.address, 1000n); - expect(retrieved).toBeDefined(); - expect(retrieved?.address.equals(instance.address)).toBe(true); + await expect(store.addContractInstances([instance], BlockNumber(2))).rejects.toThrow(/already exists/); }); it('does not duplicate logs when addLogs is called twice with same block', async () => { diff --git a/yarn-project/archiver/src/store/kv_archiver_store.ts b/yarn-project/archiver/src/store/kv_archiver_store.ts index 723172fdcb53..36e27644f0ae 100644 --- a/yarn-project/archiver/src/store/kv_archiver_store.ts +++ b/yarn-project/archiver/src/store/kv_archiver_store.ts @@ -16,11 +16,10 @@ import { import type { CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; import type { ContractClassPublic, + ContractClassPublicWithCommitment, ContractDataSource, ContractInstanceUpdateWithAddress, ContractInstanceWithAddress, - ExecutablePrivateFunctionWithMembershipProof, - UtilityFunctionWithMembershipProof, } from '@aztec/stdlib/contract'; import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client'; @@ -37,7 +36,7 @@ import { ContractInstanceStore } from './contract_instance_store.js'; import { LogStore } from './log_store.js'; import { MessageStore } from './message_store.js'; -export const ARCHIVER_DB_VERSION = 5; +export const ARCHIVER_DB_VERSION = 6; export const MAX_FUNCTION_SIGNATURES = 1000; export const MAX_FUNCTION_NAME_LEN = 256; @@ -167,19 +166,14 @@ export class KVArchiverDataStore implements ContractDataSource { /** * Add new contract classes from an L2 block to the store's list. - * @param data - List of contract classes to be added. - * @param bytecodeCommitments - Bytecode commitments for the contract classes. + * @param data - List of contract classes (with bytecode commitments) to be added. * @param blockNumber - Number of the L2 block the contracts were registered in. * @returns True if the operation is successful. */ - async addContractClasses( - data: ContractClassPublic[], - bytecodeCommitments: Fr[], - blockNumber: BlockNumber, - ): Promise { + async addContractClasses(data: ContractClassPublicWithCommitment[], blockNumber: BlockNumber): Promise { return ( await Promise.all( - data.map((c, i) => this.#contractClassStore.addContractClass(c, bytecodeCommitments[i], blockNumber)), + data.map(c => this.#contractClassStore.addContractClass(c, c.publicBytecodeCommitment, blockNumber)), ) ).every(Boolean); } @@ -194,15 +188,6 @@ export class KVArchiverDataStore implements ContractDataSource { return this.#contractClassStore.getBytecodeCommitment(contractClassId); } - /** Adds private functions to a contract class. */ - addFunctions( - contractClassId: Fr, - privateFunctions: ExecutablePrivateFunctionWithMembershipProof[], - utilityFunctions: UtilityFunctionWithMembershipProof[], - ): Promise { - return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, utilityFunctions); - } - /** * Add new contract instances from an L2 block to the store's list. * @param data - List of contract instances to be added. diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts index 37f3adca164f..f51448cf39d7 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -199,6 +199,7 @@ describe('aztec node', () => { epochCache, getPackageVersion() ?? '', new TestCircuitVerifier(), + new TestCircuitVerifier(), ); }); @@ -716,6 +717,7 @@ describe('aztec node', () => { epochCache, getPackageVersion() ?? '', new TestCircuitVerifier(), + new TestCircuitVerifier(), undefined, undefined, undefined, @@ -904,6 +906,7 @@ describe('aztec node', () => { epochCache, getPackageVersion() ?? '', new TestCircuitVerifier(), + new TestCircuitVerifier(), undefined, undefined, undefined, diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index be709fdc6557..2836f700a4c1 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -1,5 +1,6 @@ import { Archiver, createArchiver } from '@aztec/archiver'; -import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@aztec/bb-prover'; +import { BBCircuitVerifier, BatchChonkVerifier, QueuedIVCVerifier } from '@aztec/bb-prover'; +import { TestCircuitVerifier } from '@aztec/bb-prover/test'; import { type BlobClientInterface, createBlobClientWithFileStores } from '@aztec/blob-client/client'; import { Blob } from '@aztec/blob-lib'; import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants'; @@ -8,6 +9,7 @@ import { createEthereumChain } from '@aztec/ethereum/chain'; import { getPublicClient, makeL1HttpTransport } from '@aztec/ethereum/client'; import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts'; import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses'; +import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils'; import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types'; import { chunkBy, compactArray, pick, unique } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; @@ -151,7 +153,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { protected readonly globalVariableBuilder: GlobalVariableBuilderInterface, protected readonly epochCache: EpochCacheInterface, protected readonly packageVersion: string, - private proofVerifier: ClientProtocolCircuitVerifier, + private peerProofVerifier: ClientProtocolCircuitVerifier, + private rpcProofVerifier: ClientProtocolCircuitVerifier, private telemetry: TelemetryClient = getTelemetryClient(), private log = createLogger('node'), private blobClient?: BlobClientInterface, @@ -173,6 +176,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { } } + /** @internal Exposed for testing — returns the RPC proof verifier. */ + public getProofVerifier(): ClientProtocolCircuitVerifier { + return this.rpcProofVerifier; + } + public async getWorldStateSyncStatus(): Promise { const status = await this.worldStateSynchronizer.status(); return status.syncSummary; @@ -308,10 +316,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { options.prefilledPublicData, telemetry, ); - const circuitVerifier = - config.realProofs || config.debugForceTxProofVerification - ? await BBCircuitVerifier.new(config) - : new TestCircuitVerifier(config.proverTestVerificationDelayMs); + const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification; + let peerProofVerifier: ClientProtocolCircuitVerifier; + let rpcProofVerifier: ClientProtocolCircuitVerifier; + if (useRealVerifiers) { + peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer'); + const rpcVerifier = await BBCircuitVerifier.new(config); + rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers); + } else { + peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs); + rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs); + } let debugLogStore: DebugLogStore; if (!config.realProofs) { @@ -325,8 +340,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { debugLogStore = new NullDebugLogStore(); } - const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier); - const proverOnly = config.enableProverNode && config.disableValidator; if (proverOnly) { log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems'); @@ -336,7 +349,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { const p2pClient = await createP2PClient( config, archiver, - proofVerifier, + peerProofVerifier, worldStateSynchronizer, epochCache, packageVersion, @@ -458,6 +471,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { }) .catch(err => log.error('Failed to start p2p services after archiver sync', err)); + const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, { + l1Contracts: config.l1Contracts, + ethereumSlotDuration: config.ethereumSlotDuration, + rollupVersion: BigInt(config.rollupVersion), + l1GenesisTime, + slotDuration: Number(slotDuration), + }); + // Validator enabled, create/start relevant service let sequencer: SequencerClient | undefined; let slasherClient: SlasherClientInterface | undefined; @@ -495,6 +516,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { { telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() }, ); + // Create a funder L1TxUtils from the keystore funding account (if configured) + const fundingSigner = keyStoreManager?.createFundingSigner(); + let funderL1TxUtils: L1TxUtils | undefined; + if (fundingSigner) { + const [funder] = await createL1TxUtilsFromSigners( + publicClient, + [fundingSigner], + { ...config, scope: 'sequencer' }, + { telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider }, + ); + funderL1TxUtils = funder; + } + // Create and start the sequencer client const checkpointsBuilder = new CheckpointsBuilder( { ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit }, @@ -509,6 +543,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { ...deps, epochCache, l1TxUtils, + funderL1TxUtils, validatorClient, p2pClient, worldStateSynchronizer, @@ -520,6 +555,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { dateProvider, blobClient, nodeKeyStore: keyStoreManager!, + globalVariableBuilder, }); } @@ -553,13 +589,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { } } - const globalVariableBuilder = new GlobalVariableBuilder({ - ...config, - rollupVersion: BigInt(config.rollupVersion), - l1GenesisTime, - slotDuration: Number(slotDuration), - }); - const node = new AztecNodeService( config, p2pClient, @@ -578,7 +607,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { globalVariableBuilder, epochCache, packageVersion, - proofVerifier, + peerProofVerifier, + rpcProofVerifier, telemetry, log, blobClient, @@ -925,7 +955,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { await tryStop(this.validatorsSentinel); await tryStop(this.epochPruneWatcher); await tryStop(this.slasherClient); - await tryStop(this.proofVerifier); + await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]); await tryStop(this.sequencer); await tryStop(this.proverNode); await tryStop(this.p2pClient); @@ -1315,7 +1345,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { { isSimulation, skipFeeEnforcement }: { isSimulation?: boolean; skipFeeEnforcement?: boolean } = {}, ): Promise { const db = this.worldStateSynchronizer.getCommitted(); - const verifier = isSimulation ? undefined : this.proofVerifier; + const verifier = isSimulation ? undefined : this.rpcProofVerifier; // We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field) const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot(); @@ -1364,7 +1394,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { archiver.updateConfig(config); } if (newConfig.realProofs !== this.config.realProofs) { - this.proofVerifier = config.realProofs ? await BBCircuitVerifier.new(newConfig) : new TestCircuitVerifier(); + await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]); + if (newConfig.realProofs) { + this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer'); + const rpcVerifier = await BBCircuitVerifier.new(newConfig); + this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers); + } else { + this.peerProofVerifier = new TestCircuitVerifier(); + this.rpcProofVerifier = new TestCircuitVerifier(); + } } this.config = newConfig; diff --git a/yarn-project/aztec-node/src/sentinel/sentinel.test.ts b/yarn-project/aztec-node/src/sentinel/sentinel.test.ts index 671de2cff77c..b7cbd09e118c 100644 --- a/yarn-project/aztec-node/src/sentinel/sentinel.test.ts +++ b/yarn-project/aztec-node/src/sentinel/sentinel.test.ts @@ -852,6 +852,21 @@ describe('sentinel', () => { expect(result).toBe(false); }); + + it('should not divide by zero when an epoch has total 0 (treat as not inactive)', async () => { + const mockPerformance = [ + { epoch: EpochNumber(5), missed: 8, total: 10 }, // 80% missed (inactive) + { epoch: EpochNumber(4), missed: 8, total: 0 }, // total 0 -> guard: do not divide; treat as not inactive + { epoch: EpochNumber(3), missed: 8, total: 10 }, // 80% missed (inactive) + ]; + + jest.spyOn(store, 'getProvenPerformance').mockResolvedValue(mockPerformance); + + const result = await sentinel.checkPastInactivity(validator1, EpochNumber(6), 3); + + // Epoch 4 has total 0 so it must not count as inactive; we don't have 3 consecutive inactive epochs + expect(result).toBe(false); + }); }); describe('handleProvenPerformance with consecutive epochs', () => { diff --git a/yarn-project/aztec-node/src/sentinel/sentinel.ts b/yarn-project/aztec-node/src/sentinel/sentinel.ts index 8c07e06056d3..11c31f71e65d 100644 --- a/yarn-project/aztec-node/src/sentinel/sentinel.ts +++ b/yarn-project/aztec-node/src/sentinel/sentinel.ts @@ -219,7 +219,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme // Check that we have at least requiredConsecutiveEpochs and that all of them are above the inactivity threshold return pastEpochs .slice(0, requiredConsecutiveEpochs) - .every(p => p.missed / p.total >= this.config.slashInactivityTargetPercentage); + .every(p => (p.total === 0 ? false : p.missed / p.total >= this.config.slashInactivityTargetPercentage)); } protected async handleProvenPerformance(epoch: EpochNumber, performance: ValidatorsEpochPerformance) { diff --git a/yarn-project/aztec/src/testing/anvil_test_watcher.ts b/yarn-project/aztec/src/testing/anvil_test_watcher.ts index 44ef6662a9c8..4eabc6583949 100644 --- a/yarn-project/aztec/src/testing/anvil_test_watcher.ts +++ b/yarn-project/aztec/src/testing/anvil_test_watcher.ts @@ -130,7 +130,7 @@ export class AnvilTestWatcher { return; } - const l1Time = (await this.cheatcodes.timestamp()) * 1000; + const l1Time = (await this.cheatcodes.lastBlockTimestamp()) * 1000; const wallTime = this.dateProvider.now(); if (l1Time > wallTime) { this.logger.warn(`L1 is ahead of wall time. Syncing wall time to L1 time`); diff --git a/yarn-project/aztec/src/testing/cheat_codes.ts b/yarn-project/aztec/src/testing/cheat_codes.ts index 94ebe0b87046..14413fd4ddd3 100644 --- a/yarn-project/aztec/src/testing/cheat_codes.ts +++ b/yarn-project/aztec/src/testing/cheat_codes.ts @@ -72,7 +72,7 @@ export class CheatCodes { * @param duration - The duration to advance time by (in seconds) */ async warpL2TimeAtLeastBy(sequencerClient: SequencerClient, node: AztecNode, duration: bigint | number) { - const currentTimestamp = await this.eth.timestamp(); + const currentTimestamp = await this.eth.lastBlockTimestamp(); const targetTimestamp = BigInt(currentTimestamp) + BigInt(duration); await this.warpL2TimeAtLeastTo(sequencerClient, node, targetTimestamp); } diff --git a/yarn-project/bb-prover/package.json b/yarn-project/bb-prover/package.json index 04774fdf6ea8..5e08beeba10c 100644 --- a/yarn-project/bb-prover/package.json +++ b/yarn-project/bb-prover/package.json @@ -80,6 +80,7 @@ "@aztec/telemetry-client": "workspace:^", "@aztec/world-state": "workspace:^", "commander": "^12.1.0", + "msgpackr": "^1.11.2", "pako": "^2.1.0", "source-map-support": "^0.5.21", "tslib": "^2.4.0" diff --git a/yarn-project/bb-prover/src/config.ts b/yarn-project/bb-prover/src/config.ts index 60a33c9a67b6..19389258a3c9 100644 --- a/yarn-project/bb-prover/src/config.ts +++ b/yarn-project/bb-prover/src/config.ts @@ -3,8 +3,19 @@ export interface BBConfig { bbWorkingDirectory: string; /** Whether to skip tmp dir cleanup for debugging purposes */ bbSkipCleanup: boolean; + /** Max concurrent verifications for the RPC verifier (QueuedIVCVerifier). */ numConcurrentIVCVerifiers: number; + /** Thread count for the RPC IVC verifier. */ bbIVCConcurrency: number; + /** + * Upper bound on proofs per batch for the peer chonk batch verifier. + * Proofs are verified immediately as they arrive — this only caps how many + * can accumulate while a batch is already being processed. + * Default 16: at 4 cores, a full batch of 16 verifies in ~245ms wall time. + */ + bbChonkVerifyMaxBatch: number; + /** Thread count for the peer batch verifier parallel reduce. Default 6 to leave cores for the rest of the node. */ + bbChonkVerifyConcurrency: number; } export interface ACVMConfig { diff --git a/yarn-project/bb-prover/src/verifier/batch_chonk_verifier.ts b/yarn-project/bb-prover/src/verifier/batch_chonk_verifier.ts new file mode 100644 index 000000000000..b64811b9240c --- /dev/null +++ b/yarn-project/bb-prover/src/verifier/batch_chonk_verifier.ts @@ -0,0 +1,276 @@ +import { BackendType, Barretenberg } from '@aztec/bb.js'; +import { FifoFrameReader } from '@aztec/foundation/fifo'; +import { createLogger } from '@aztec/foundation/log'; +import { SerialQueue } from '@aztec/foundation/queue'; +import { Timer } from '@aztec/foundation/timer'; +import { ProtocolCircuitVks } from '@aztec/noir-protocol-circuits-types/server/vks'; +import type { ClientProtocolCircuitVerifier, IVCProofVerificationResult } from '@aztec/stdlib/interfaces/server'; +import type { Tx } from '@aztec/stdlib/tx'; + +import { Unpackr } from 'msgpackr'; +import { execFile } from 'node:child_process'; +import { unlinkSync } from 'node:fs'; +import { unlink } from 'node:fs/promises'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { promisify } from 'node:util'; + +import type { BBConfig } from '../config.js'; + +const execFileAsync = promisify(execFile); + +/** Result from the FIFO, matching the C++ VerifyResult struct. */ +interface FifoVerifyResult { + request_id: number; + status: number; + error_message: string; + time_in_verify_ms: number; +} + +/** Maps client protocol artifacts used for chonk verification to VK indices. */ +const CHONK_VK_ARTIFACTS = ['HidingKernelToRollup', 'HidingKernelToPublic'] as const; + +interface PendingRequest { + resolve: (result: IVCProofVerificationResult) => void; + reject: (error: Error) => void; + totalTimer: Timer; +} + +/** + * Batch verifier for Chonk IVC proofs. Uses the bb batch verifier service + * which batches IPA verification into a single SRS MSM for better throughput. + * + * Architecture: + * - Spawns a persistent `bb msgpack run` process via Barretenberg (native backend) + * - Sends proofs via the msgpack RPC protocol (ChonkBatchVerifierQueue) + * - Receives results via a named FIFO pipe (async, out-of-order) + * - Bisects batch failures to isolate individual bad proofs + */ +export class BatchChonkVerifier implements ClientProtocolCircuitVerifier { + private bb!: Barretenberg; + private fifoPath: string; + private nextRequestId = 0; + private pendingRequests = new Map(); + private sendQueue: SerialQueue; + private fifoReader: FifoFrameReader; + private logger = createLogger('bb-prover:batch_chonk_verifier'); + /** Maps artifact name to VK index in the batch verifier. */ + private vkIndexMap = new Map(); + /** Bound cleanup handler for process exit signals. */ + private exitCleanup: (() => void) | null = null; + + private constructor( + private config: Pick & Partial>, + private vkBuffers: Uint8Array[], + private batchSize: number, + private label: string, + ) { + this.fifoPath = path.join(os.tmpdir(), `bb-batch-${label}-${process.pid}-${Date.now()}.fifo`); + this.fifoReader = new FifoFrameReader(); + this.sendQueue = new SerialQueue(); + this.sendQueue.start(1); + } + + /** Create and start a BatchChonkVerifier using the protocol circuit VKs. */ + static async new(config: BBConfig, batchSize: number, label: string): Promise { + const vkBuffers: Uint8Array[] = []; + const vkIndexMap = new Map(); + for (const artifact of CHONK_VK_ARTIFACTS) { + const vk = ProtocolCircuitVks[artifact]; + if (!vk) { + throw new Error(`Missing VK for ${artifact}`); + } + vkIndexMap.set(artifact, vkBuffers.length); + vkBuffers.push(vk.keyAsBytes); + } + const verifier = new BatchChonkVerifier(config, vkBuffers, batchSize, label); + verifier.vkIndexMap = vkIndexMap; + await verifier.start(); + return verifier; + } + + /** Create and start a BatchChonkVerifier with custom VKs (for testing). */ + static async newForTesting( + config: Pick & Partial>, + vks: Uint8Array[], + batchSize: number, + ): Promise { + const verifier = new BatchChonkVerifier(config, vks, batchSize, 'test'); + for (let i = 0; i < vks.length; i++) { + verifier.vkIndexMap.set(String(i), i); + } + await verifier.start(); + return verifier; + } + + private async start(): Promise { + this.logger.info('Starting BatchChonkVerifier'); + + this.bb = await Barretenberg.new({ + bbPath: this.config.bbBinaryPath, + backend: BackendType.NativeUnixSocket, + }); + await this.bb.initSRSChonk(); + + await execFileAsync('mkfifo', [this.fifoPath]); + this.registerExitCleanup(); + + await this.bb.chonkBatchVerifierStart({ + vks: this.vkBuffers, + numCores: this.config.bbChonkVerifyConcurrency || 0, + batchSize: this.batchSize, + fifoPath: this.fifoPath, + }); + + this.startFifoReader(); + this.logger.info('BatchChonkVerifier started', { fifoPath: this.fifoPath }); + } + + public verifyProof(tx: Tx): Promise { + const circuit = tx.data.forPublic ? 'HidingKernelToPublic' : 'HidingKernelToRollup'; + const vkIndex = this.vkIndexMap.get(circuit); + if (vkIndex === undefined) { + throw new Error(`No VK index for circuit ${circuit}`); + } + const proofWithPubInputs = tx.chonkProof.attachPublicInputs(tx.data.publicInputs().toFields()); + const proofFields = proofWithPubInputs.fieldsWithPublicInputs.map(f => f.toBuffer()); + return this.enqueueProof(vkIndex, proofFields); + } + + /** Enqueue raw proof fields for verification. Used directly by tests with custom VKs. */ + public enqueueProof(vkIndex: number, proofFields: Uint8Array[]): Promise { + const totalTimer = new Timer(); + const requestId = this.nextRequestId++; + + const resultPromise = new Promise((resolve, reject) => { + this.pendingRequests.set(requestId, { resolve, reject, totalTimer }); + }); + + void this.sendQueue + .put(async () => { + await this.bb.chonkBatchVerifierQueue({ + requestId, + vkIndex, + proofFields, + }); + }) + .catch(err => { + const pending = this.pendingRequests.get(requestId); + if (pending) { + this.pendingRequests.delete(requestId); + pending.reject(err instanceof Error ? err : new Error(String(err))); + } + }); + + return resultPromise; + } + + public async stop(): Promise { + this.logger.info('Stopping BatchChonkVerifier'); + + // Stop accepting new proofs + await this.sendQueue.end(); + + // Stop the bb service (flushes remaining proofs) + try { + await this.bb.chonkBatchVerifierStop({}); + } catch (err) { + this.logger.warn(`Error stopping batch verifier service: ${err}`); + } + + // Stop FIFO reader + this.fifoReader.stop(); + + // Clean up FIFO file and deregister exit handler + await unlink(this.fifoPath).catch(() => {}); + this.deregisterExitCleanup(); + + // Reject any remaining pending requests + for (const [id, pending] of this.pendingRequests) { + pending.reject(new Error('BatchChonkVerifier stopped')); + this.pendingRequests.delete(id); + } + + // Destroy bb process + await this.bb.destroy(); + + this.logger.info('BatchChonkVerifier stopped'); + } + + private startFifoReader(): void { + const unpackr = new Unpackr({ useRecords: false }); + + this.fifoReader.on('frame', (payload: Buffer) => { + try { + const result = unpackr.unpack(payload) as FifoVerifyResult; + this.handleResult(result); + } catch (err) { + this.logger.error(`FIFO: failed to decode msgpack result: ${err}`); + } + }); + + this.fifoReader.on('error', (err: Error) => { + this.logger.error(`FIFO reader error: ${err}`); + }); + + this.fifoReader.on('end', () => { + this.logger.debug('FIFO reader: stream ended'); + for (const [id, pending] of this.pendingRequests) { + pending.reject(new Error('FIFO stream ended unexpectedly')); + this.pendingRequests.delete(id); + } + }); + + this.fifoReader.start(this.fifoPath); + } + + private handleResult(result: FifoVerifyResult): void { + const pending = this.pendingRequests.get(result.request_id); + if (!pending) { + this.logger.warn(`Received result for unknown request_id=${result.request_id}`); + return; + } + this.pendingRequests.delete(result.request_id); + + const valid = result.status === 0; // VerifyStatus::OK + const durationMs = result.time_in_verify_ms; + const totalDurationMs = pending.totalTimer.ms(); + + const ivcResult: IVCProofVerificationResult = { valid, durationMs, totalDurationMs }; + + if (!valid) { + this.logger.warn(`Proof verification failed for request_id=${result.request_id}: ${result.error_message}`); + } else { + this.logger.debug(`Proof verified`, { + requestId: result.request_id, + durationMs: Math.ceil(durationMs), + totalDurationMs: Math.ceil(totalDurationMs), + }); + } + + pending.resolve(ivcResult); + } + + private registerExitCleanup(): void { + // Signal handlers must be synchronous — unlinkSync is intentional here + this.exitCleanup = () => { + try { + unlinkSync(this.fifoPath); + } catch { + /* ignore */ + } + }; + process.on('exit', this.exitCleanup); + process.on('SIGINT', this.exitCleanup); + process.on('SIGTERM', this.exitCleanup); + } + + private deregisterExitCleanup(): void { + if (this.exitCleanup) { + process.removeListener('exit', this.exitCleanup); + process.removeListener('SIGINT', this.exitCleanup); + process.removeListener('SIGTERM', this.exitCleanup); + this.exitCleanup = null; + } + } +} diff --git a/yarn-project/bb-prover/src/verifier/index.ts b/yarn-project/bb-prover/src/verifier/index.ts index 1ce2f3a7e41a..229e41274d6e 100644 --- a/yarn-project/bb-prover/src/verifier/index.ts +++ b/yarn-project/bb-prover/src/verifier/index.ts @@ -1,2 +1,3 @@ +export * from './batch_chonk_verifier.js'; export * from './bb_verifier.js'; export * from './queued_chonk_verifier.js'; diff --git a/yarn-project/bb-prover/src/verifier/queued_chonk_verifier.ts b/yarn-project/bb-prover/src/verifier/queued_chonk_verifier.ts index 4eeca0d042e5..c7d62565fa6a 100644 --- a/yarn-project/bb-prover/src/verifier/queued_chonk_verifier.ts +++ b/yarn-project/bb-prover/src/verifier/queued_chonk_verifier.ts @@ -16,8 +16,6 @@ import { import { createHistogram } from 'node:perf_hooks'; -import type { BBConfig } from '../config.js'; - class IVCVerifierMetrics { private ivcVerificationHistogram: Histogram; private ivcTotalVerificationHistogram: Histogram; @@ -86,15 +84,15 @@ export class QueuedIVCVerifier implements ClientProtocolCircuitVerifier { private metrics: IVCVerifierMetrics; public constructor( - config: BBConfig, private verifier: ClientProtocolCircuitVerifier, + concurrency: number, private telemetry: TelemetryClient = getTelemetryClient(), private logger = createLogger('bb-prover:queued_chonk_verifier'), ) { this.metrics = new IVCVerifierMetrics(this.telemetry, 'QueuedIVCVerifier'); this.queue = new SerialQueue(); - this.logger.info(`Starting QueuedIVCVerifier with ${config.numConcurrentIVCVerifiers} concurrent verifiers`); - this.queue.start(config.numConcurrentIVCVerifiers); + this.logger.info(`Starting QueuedIVCVerifier with ${concurrency} concurrent verifiers`); + this.queue.start(concurrency); } public async verifyProof(tx: Tx): Promise { @@ -103,7 +101,8 @@ export class QueuedIVCVerifier implements ClientProtocolCircuitVerifier { return result; } - stop(): Promise { - return this.queue.end(); + async stop(): Promise { + await this.queue.end(); + await this.verifier.stop(); } } diff --git a/yarn-project/bootstrap.sh b/yarn-project/bootstrap.sh index fdf24a891720..2509a7628e4e 100755 --- a/yarn-project/bootstrap.sh +++ b/yarn-project/bootstrap.sh @@ -248,6 +248,7 @@ function bench_cmds { echo "$hash:ISOLATE=1:CPUS=10:MEM=16g:LOG_LEVEL=silent BENCH_OUTPUT=bench-out/proving_broker.bench.json yarn-project/scripts/run_test.sh prover-client/src/test/proving_broker_testbench.test.ts" echo "$hash:ISOLATE=1:CPUS=16:MEM=16g BENCH_OUTPUT=bench-out/avm_bulk_test.bench.json yarn-project/scripts/run_test.sh bb-prover/src/avm_proving_tests/avm_bulk.test.ts" echo "$hash BENCH_OUTPUT=bench-out/lightweight_checkpoint_builder.bench.json yarn-project/scripts/run_test.sh prover-client/src/light/lightweight_checkpoint_builder.bench.test.ts" + echo "$hash:ISOLATE=1:CPUS=8:MEM=32g:TIMEOUT=1200 BENCH_OUTPUT=bench-out/batch_verifier.bench.json yarn-project/scripts/run_test.sh ivc-integration/src/batch_verifier.bench.test.ts" } function release_packages { diff --git a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts index 27b0bf124dfc..5a4646542537 100644 --- a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts +++ b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts @@ -46,19 +46,19 @@ describe('e2e_cheat_codes', () => { it.each([100, 42, 99])(`setNextBlockTimestamp by %i`, async increment => { const blockNumber = await ethCheatCodes.blockNumber(); - const timestamp = await ethCheatCodes.timestamp(); + const timestamp = await ethCheatCodes.lastBlockTimestamp(); await ethCheatCodes.setNextBlockTimestamp(timestamp + increment); - expect(await ethCheatCodes.timestamp()).toBe(timestamp); + expect(await ethCheatCodes.lastBlockTimestamp()).toBe(timestamp); await ethCheatCodes.mine(); expect(await ethCheatCodes.blockNumber()).toBe(blockNumber + 1); - expect(await ethCheatCodes.timestamp()).toBe(timestamp + increment); + expect(await ethCheatCodes.lastBlockTimestamp()).toBe(timestamp + increment); }); it('setNextBlockTimestamp to a past timestamp throws', async () => { - const timestamp = await ethCheatCodes.timestamp(); + const timestamp = await ethCheatCodes.lastBlockTimestamp(); const pastTimestamp = timestamp - 1000; await expect(async () => await ethCheatCodes.setNextBlockTimestamp(pastTimestamp)).rejects.toThrow( 'Timestamp error', diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index 68711711b23b..5668ba4e3e77 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -60,7 +60,7 @@ describe('e2e_crowdfunding_and_claim', () => { } = await setup(3)); // We set the deadline to a week from now - deadline = (await cheatCodes.eth.timestamp()) + 7 * 24 * 60 * 60; + deadline = (await cheatCodes.eth.lastBlockTimestamp()) + 7 * 24 * 60 * 60; ({ contract: donationToken } = await TokenContract.deploy( wallet, diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index 37da34dd7f3c..5f09613cc7eb 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -69,7 +69,6 @@ describe('e2e_deploy_contract contract class registration', () => { expect(registeredClass!.artifactHash.toString()).toEqual(contractClass.artifactHash.toString()); expect(registeredClass!.privateFunctionsRoot.toString()).toEqual(contractClass.privateFunctionsRoot.toString()); expect(registeredClass!.packedBytecode.toString('hex')).toEqual(contractClass.packedBytecode.toString('hex')); - expect(registeredClass!.privateFunctions).toEqual([]); }); }); diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_missed_l1_slot.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_missed_l1_slot.test.ts new file mode 100644 index 000000000000..3e863302f2c3 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_missed_l1_slot.test.ts @@ -0,0 +1,152 @@ +import type { ChainMonitorEventMap } from '@aztec/ethereum/test'; +import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types'; +import { AbortError } from '@aztec/foundation/error'; +import { sleep } from '@aztec/foundation/sleep'; +import { executeTimeout } from '@aztec/foundation/timer'; +import { SequencerState } from '@aztec/sequencer-client'; +import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers'; + +import { jest } from '@jest/globals'; + +import { EpochsTestContext } from './epochs_test.js'; + +jest.setTimeout(1000 * 60 * 10); + +// Validates that the sequencer can build a block in an L2 slot even when the archiver hasn't synced +// all L1 blocks of the previous slot. This happens when an L1 slot is missed (no block produced). +// The fix relies on getSyncedL2SlotNumber using the latest synced checkpoint slot as a signal, +// bypassing the stale L1 timestamp when L1 blocks are missing. +// Regression test for https://github.com/AztecProtocol/aztec-packages/issues/14766 +describe('e2e_epochs/epochs_missed_l1_slot', () => { + let test: EpochsTestContext; + + // Use enough L1 slots per L2 slot to have room for pausing mining mid-slot. + // With 6 L1 slots per L2 slot (L1=8s, L2=48s), we have plenty of time to + // publish a checkpoint and pause mining without accidentally skipping a slot. + const L1_SLOTS_PER_L2_SLOT = 6; + + beforeEach(async () => { + test = await EpochsTestContext.setup({ + numberOfAccounts: 0, + minTxsPerBlock: 0, + aztecSlotDurationInL1Slots: L1_SLOTS_PER_L2_SLOT, + startProverNode: false, + aztecProofSubmissionEpochs: 1024, + disableAnvilTestWatcher: true, + enforceTimeTable: true, + }); + }); + + afterEach(async () => { + jest.restoreAllMocks(); + await test.teardown(); + }); + + it('builds a block after missed L1 slots when previous checkpoint is synced', async () => { + const { logger, constants, monitor, context } = test; + const eth = context.cheatCodes.eth; + const L1_BLOCK_TIME = test.L1_BLOCK_TIME_IN_S; + const L2_SLOT_DURATION = test.L2_SLOT_DURATION_IN_S; + + // Step 1: Wait for a checkpoint that's published NOT in the last L1 slot of its L2 slot. + // We need the checkpoint to land early enough that when we pause mining, the archiver's + // L1 timestamp is still in the middle of the slot (not at the end). + logger.info('Waiting for a checkpoint published early in its L2 slot...'); + const checkpointEvent = await executeTimeout( + signal => + new Promise((res, rej) => { + const handleCheckpoint = (...[ev]: ChainMonitorEventMap['checkpoint']) => { + // Skip the initial checkpoint (genesis state). + if (ev.checkpointNumber === 0) { + return; + } + const slotStart = getTimestampForSlot(ev.l2SlotNumber, constants); + const lastL1SlotStart = slotStart + BigInt(L2_SLOT_DURATION - L1_BLOCK_TIME); + if (ev.timestamp < lastL1SlotStart) { + logger.info( + `Checkpoint ${ev.checkpointNumber} in slot ${ev.l2SlotNumber} at L1 timestamp ${ev.timestamp}`, + { slotStart, lastL1SlotStart }, + ); + res(ev); + monitor.off('checkpoint', handleCheckpoint); + } else { + logger.info( + `Skipping checkpoint ${ev.checkpointNumber}: published at ${ev.timestamp} (last L1 slot starts at ${lastL1SlotStart})`, + ); + } + }; + signal.onabort = () => { + monitor.off('checkpoint', handleCheckpoint); + rej(new AbortError()); + }; + monitor.on('checkpoint', handleCheckpoint); + }), + 60_000, + 'Wait for early checkpoint', + ); + + const checkpointSlotNumber = checkpointEvent.l2SlotNumber; + const nextSlotNumber = SlotNumber(checkpointSlotNumber + 1); + const nextSlotTimestamp = Number(getTimestampForSlot(nextSlotNumber, constants)); + + logger.info(`Using checkpoint ${checkpointEvent.checkpointNumber} in L2 slot ${checkpointSlotNumber}`, { + nextSlotNumber, + nextSlotTimestamp, + }); + + // Step 2: Wait briefly for the sequencer to finish its current work cycle, then pause mining. + await sleep(1500); + + logger.info('Pausing L1 block production (simulating missed L1 slots)...'); + await eth.setAutomine(false); + await eth.setIntervalMining(0, { silent: true }); + + const frozenL1Timestamp = await eth.lastBlockTimestamp(); + logger.info(`L1 mining paused at L1 timestamp ${frozenL1Timestamp}`); + + // Step 3: Wait until the sequencer reaches PUBLISHING_CHECKPOINT during the mining pause. + // With the fix: the sequencer sees the checkpoint for slot N, so getSyncedL2SlotNumber + // returns N, checkSync passes for slot N+1, and it advances all the way to publishing. + // Without the fix: getSyncedL2SlotNumber is stuck at N-1, checkSync fails, sequencer + // stays in IDLE/SYNCHRONIZING and never reaches PUBLISHING_CHECKPOINT. + const sequencer = context.sequencer!.getSequencer(); + + logger.info('Waiting for sequencer to reach PUBLISHING_CHECKPOINT during mining pause...'); + await executeTimeout( + signal => + new Promise((res, rej) => { + const stateListener = ({ newState }: { newState: SequencerState }) => { + if (newState === SequencerState.PUBLISHING_CHECKPOINT) { + sequencer.off('state-changed', stateListener); + res(); + } + }; + signal.onabort = () => { + sequencer.off('state-changed', stateListener); + rej(new AbortError()); + }; + sequencer.on('state-changed', stateListener); + }), + L2_SLOT_DURATION * 2 * 1000, + 'Wait for sequencer to reach PUBLISHING_CHECKPOINT', + ); + + logger.info('Sequencer reached PUBLISHING_CHECKPOINT during mining pause'); + + // Step 4: Resume mining so the pending L1 tx lands and the test can clean up. + logger.info('Resuming L1 block production...'); + const resumeTimestamp = Math.floor(context.dateProvider.now() / 1000); + await eth.setNextBlockTimestamp(resumeTimestamp); + await eth.mine(); + await eth.setIntervalMining(L1_BLOCK_TIME); + + // Step 5: Wait for the next checkpoint to confirm the block was actually published. + const finalCheckpoint = CheckpointNumber(checkpointEvent.checkpointNumber + 1); + logger.info(`Waiting for checkpoint ${finalCheckpoint}...`); + await test.waitUntilCheckpointNumber(finalCheckpoint, 60); + await monitor.run(); + logger.info(`Checkpoint ${finalCheckpoint} published in slot ${monitor.l2SlotNumber}`); + + expect(monitor.checkpointNumber).toBeGreaterThanOrEqual(finalCheckpoint); + }); +}); diff --git a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts index 2b3d00198fa4..c1a2c1db8da6 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts @@ -517,7 +517,7 @@ describe('e2e_p2p_add_rollup', () => { const futureEpoch = EpochNumber.fromBigInt(500n + BigInt(await newRollup.getCurrentEpochNumber())); const futureSlot = SlotNumber.fromBigInt(BigInt(futureEpoch) * BigInt(t.ctx.aztecNodeConfig.aztecEpochDuration)); const time = await newRollup.getTimestampForSlot(futureSlot); - if (time > BigInt(await t.ctx.cheatCodes.eth.timestamp())) { + if (time > BigInt(await t.ctx.cheatCodes.eth.lastBlockTimestamp())) { await t.ctx.cheatCodes.eth.warp(Number(time)); await waitL1Block(); } diff --git a/yarn-project/end-to-end/src/e2e_publisher_funding_multi.test.ts b/yarn-project/end-to-end/src/e2e_publisher_funding_multi.test.ts new file mode 100644 index 000000000000..e48477c8fefa --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_publisher_funding_multi.test.ts @@ -0,0 +1,179 @@ +import { AztecAddress, EthAddress } from '@aztec/aztec.js/addresses'; +import { Fr } from '@aztec/aztec.js/fields'; +import type { Logger } from '@aztec/aztec.js/log'; +import type { EthCheatCodes } from '@aztec/aztec/testing'; +import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils'; +import type { PublisherManager } from '@aztec/ethereum/publisher-manager'; +import { SecretValue } from '@aztec/foundation/config'; +import { retryUntil } from '@aztec/foundation/retry'; +import type { SequencerClient } from '@aztec/sequencer-client'; +import type { TestSequencerClient } from '@aztec/sequencer-client/test'; + +import { jest } from '@jest/globals'; +import { mkdtemp, rm, writeFile } from 'fs/promises'; +import 'jest-extended'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { type Hex, parseEther } from 'viem'; +import { privateKeyToAccount } from 'viem/accounts'; + +import { getPrivateKeyFromIndex, setup } from './fixtures/utils.js'; + +// Key indices from the test MNEMONIC (all pre-funded by Anvil): +// 0 = L1 contract deployer (not in keystore) +// 1 = validator attester + publisher 1 EOA +// 2 = funding account +// 3 = publisher 2 EOA +const DEPLOYER_KEY_INDEX = 0; +const PUBLISHER1_KEY_INDEX = 1; +const FUNDER_KEY_INDEX = 2; +const PUBLISHER2_KEY_INDEX = 3; + +const toPrivateKeyHex = (index: number): Hex => { + const buf = getPrivateKeyFromIndex(index); + if (!buf) { + throw new Error(`Failed to derive private key for index ${index}`); + } + return `0x${buf.toString('hex')}`; +}; + +const FUNDING_THRESHOLD = parseEther('2'); +const FUNDING_AMOUNT = parseEther('2.1'); + +describe('e2e_publisher_funding_multi', () => { + jest.setTimeout(5 * 60 * 1000); + + let logger: Logger; + let publisherManager: PublisherManager; + let ethCheatCodes: EthCheatCodes; + let teardown: () => Promise; + let keyStoreDirectory: string; + + beforeAll(async () => { + const attesterKey = toPrivateKeyHex(PUBLISHER1_KEY_INDEX); + const publisherKey1 = attesterKey; + const publisherKey2 = toPrivateKeyHex(PUBLISHER2_KEY_INDEX); + const funderKey = toPrivateKeyHex(FUNDER_KEY_INDEX); + const attesterAddress = privateKeyToAccount(attesterKey).address; + + // Write keystore JSON with two publishers and a fundingAccount + keyStoreDirectory = await mkdtemp(join(tmpdir(), 'publisher-funding-multi-')); + const keystore = { + schemaVersion: 1, + validators: [ + { + attester: attesterKey, + publisher: [publisherKey1, publisherKey2], + coinbase: EthAddress.fromNumber(42).toChecksumString(), + feeRecipient: AztecAddress.fromNumber(42).toString(), + }, + ], + fundingAccount: funderKey, + }; + await writeFile(join(keyStoreDirectory, 'keystore.json'), JSON.stringify(keystore, null, 2)); + + // Stake the validator on L1 so the sequencer can propose blocks + const initialValidators = [ + { + attester: EthAddress.fromString(attesterAddress), + withdrawer: EthAddress.fromString(attesterAddress), + privateKey: attesterKey as Hex, + bn254SecretKey: new SecretValue(Fr.random().toBigInt()), + }, + ]; + + let sequencerClient: SequencerClient | undefined; + ({ + teardown, + logger, + sequencer: sequencerClient, + ethCheatCodes, + } = await setup(0, { + initialValidators, + keyStoreDirectory, + publisherFundingThreshold: FUNDING_THRESHOLD, + publisherFundingAmount: FUNDING_AMOUNT, + minTxsPerBlock: 0, + l1PublisherKey: new SecretValue(toPrivateKeyHex(DEPLOYER_KEY_INDEX)), + })); + + publisherManager = (sequencerClient! as TestSequencerClient).publisherManager; + }); + + afterAll(async () => { + await teardown(); + await rm(keyStoreDirectory, { recursive: true, force: true }); + }); + + it('funds both publishers when balances drop below threshold', async () => { + const publishers: L1TxUtils[] = (publisherManager as any).publishers; + const funder: L1TxUtils | undefined = (publisherManager as any).funder; + + expect(publishers.length).toBe(2); + expect(funder).toBeDefined(); + + const publisher1Address = publishers[0].getSenderAddress(); + const publisher2Address = publishers[1].getSenderAddress(); + const funderAddress = funder!.getSenderAddress(); + logger.info(`Publisher1: ${publisher1Address}, Publisher2: ${publisher2Address}, Funder: ${funderAddress}`); + + // Set both publisher balances below threshold + const LOW_BALANCE = parseEther('0.1'); + await ethCheatCodes.setBalance(publisher1Address, LOW_BALANCE); + await ethCheatCodes.setBalance(publisher2Address, LOW_BALANCE); + // Give funder plenty of ETH + await ethCheatCodes.setBalance(funderAddress, parseEther('100')); + + const funderBalanceBefore = await ethCheatCodes.getBalance(funderAddress); + + // The RunningPromise checks funding every 2 minutes, so we need to wait long enough + // for the next cycle to detect the low balances and fund both publishers. + await retryUntil( + async () => { + const balance1 = await ethCheatCodes.getBalance(publisher1Address); + const balance2 = await ethCheatCodes.getBalance(publisher2Address); + return balance1 > LOW_BALANCE && balance2 > LOW_BALANCE ? true : undefined; + }, + 'waiting for both publishers to be funded', + 180, + 1, + ); + + const publisher1BalanceAfter = await ethCheatCodes.getBalance(publisher1Address); + const publisher2BalanceAfter = await ethCheatCodes.getBalance(publisher2Address); + const funderBalanceAfter = await ethCheatCodes.getBalance(funderAddress); + const funderSpent = funderBalanceBefore - funderBalanceAfter; + + logger.info(`Publisher1 balance after: ${publisher1BalanceAfter} (was ${LOW_BALANCE})`); + logger.info(`Publisher2 balance after: ${publisher2BalanceAfter} (was ${LOW_BALANCE})`); + logger.info(`Funder spent: ${funderSpent} (expected ~${2n * FUNDING_AMOUNT})`); + + expect(publisher1BalanceAfter).toBeGreaterThan(LOW_BALANCE); + expect(publisher2BalanceAfter).toBeGreaterThan(LOW_BALANCE); + // Both publishers should now be above the funding threshold + expect(publisher1BalanceAfter).toBeGreaterThanOrEqual(FUNDING_THRESHOLD); + expect(publisher2BalanceAfter).toBeGreaterThanOrEqual(FUNDING_THRESHOLD); + // Funder should have sent 2 * FUNDING_AMOUNT plus gas costs (single multicall) + expect(funderSpent).toBeGreaterThanOrEqual(2n * FUNDING_AMOUNT); + + // Second round: after funding, publishers are above threshold. The active publisher will + // spend gas publishing blocks and eventually drop below threshold again, triggering re-funding + // for that one publisher. + const funderBalanceBefore2 = await ethCheatCodes.getBalance(funderAddress); + logger.info(`Waiting for second funding round`); + + await retryUntil( + async () => { + const spent = funderBalanceBefore2 - (await ethCheatCodes.getBalance(funderAddress)); + return spent >= FUNDING_AMOUNT ? true : undefined; + }, + 'waiting for second funding round', + 180, + 1, + ); + + const funderSpent2 = funderBalanceBefore2 - (await ethCheatCodes.getBalance(funderAddress)); + logger.info(`Second funding round: funder spent ${funderSpent2} (expected ~${FUNDING_AMOUNT})`); + expect(funderSpent2).toBeGreaterThanOrEqual(FUNDING_AMOUNT); + }); +}); diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index dbf533d16e94..ece38c7340aa 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -470,7 +470,7 @@ describe('e2e_synching', () => { for (const checkpoint of checkpoints) { const lastBlock = checkpoint.blocks.at(-1)!; const targetTime = Number(lastBlock.header.globalVariables.timestamp) - ETHEREUM_SLOT_DURATION; - while ((await cheatCodes.eth.timestamp()) < targetTime) { + while ((await cheatCodes.eth.lastBlockTimestamp()) < targetTime) { await cheatCodes.eth.mine(); } // If it breaks here, first place you should look is the pruning. diff --git a/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts b/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts index 59f38bfeedea..7dc8268ce083 100644 --- a/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts @@ -4,12 +4,7 @@ import { AztecAddress, EthAddress } from '@aztec/aztec.js/addresses'; import { type Logger, createLogger } from '@aztec/aztec.js/log'; import type { AztecNode } from '@aztec/aztec.js/node'; import { CheatCodes } from '@aztec/aztec/testing'; -import { - BBCircuitVerifier, - type ClientProtocolCircuitVerifier, - QueuedIVCVerifier, - TestCircuitVerifier, -} from '@aztec/bb-prover'; +import type { ClientProtocolCircuitVerifier } from '@aztec/bb-prover'; import { BackendType, Barretenberg } from '@aztec/bb.js'; import type { DeployAztecL1ContractsReturnType } from '@aztec/ethereum/deploy-aztec-l1-contracts'; import { Buffer32 } from '@aztec/foundation/buffer'; @@ -68,7 +63,10 @@ export class FullProverTest { private provenComponents: ProvenSetup[] = []; private bbConfigCleanup?: () => Promise; private acvmConfigCleanup?: () => Promise; - circuitProofVerifier?: ClientProtocolCircuitVerifier; + /** Returns the proof verifier from the prover node (for test assertions). */ + get circuitProofVerifier(): ClientProtocolCircuitVerifier | undefined { + return this.proverAztecNode?.getProofVerifier(); + } provenAsset!: TokenContract; context!: EndToEndContext; private proverAztecNode!: AztecNodeService; @@ -170,9 +168,6 @@ export class FullProverTest { await Barretenberg.initSingleton({ backend: BackendType.NativeUnixSocket }); - const verifier = await BBCircuitVerifier.new(bbConfig); - this.circuitProofVerifier = new QueuedIVCVerifier(bbConfig, verifier); - this.logger.debug(`Configuring the node for real proofs...`); await this.aztecNodeAdmin.setConfig({ realProofs: true, @@ -180,7 +175,6 @@ export class FullProverTest { }); } else { this.logger.debug(`Configuring the node min txs per block ${this.minNumberOfTxsPerBlock}...`); - this.circuitProofVerifier = new TestCircuitVerifier(); await this.aztecNodeAdmin.setConfig({ minTxsPerBlock: this.minNumberOfTxsPerBlock, }); diff --git a/yarn-project/end-to-end/src/fixtures/get_bb_config.ts b/yarn-project/end-to-end/src/fixtures/get_bb_config.ts index 9ede6ef6a1e7..4985664b301c 100644 --- a/yarn-project/end-to-end/src/fixtures/get_bb_config.ts +++ b/yarn-project/end-to-end/src/fixtures/get_bb_config.ts @@ -13,8 +13,10 @@ const { BB_SKIP_CLEANUP = '', TEMP_DIR = tmpdir(), BB_WORKING_DIRECTORY = '', - BB_NUM_IVC_VERIFIERS = '1', + BB_NUM_IVC_VERIFIERS = '8', BB_IVC_CONCURRENCY = '1', + BB_CHONK_VERIFY_MAX_BATCH = '16', + BB_CHONK_VERIFY_BATCH_CONCURRENCY = '6', } = process.env; export const getBBConfig = async ( @@ -41,16 +43,15 @@ export const getBBConfig = async ( const bbSkipCleanup = ['1', 'true'].includes(BB_SKIP_CLEANUP); const cleanup = bbSkipCleanup ? () => Promise.resolve() : () => tryRmDir(directoryToCleanup); - const numIvcVerifiers = Number(BB_NUM_IVC_VERIFIERS); - const ivcConcurrency = Number(BB_IVC_CONCURRENCY); - return { bbSkipCleanup, bbBinaryPath, bbWorkingDirectory, cleanup, - numConcurrentIVCVerifiers: numIvcVerifiers, - bbIVCConcurrency: ivcConcurrency, + numConcurrentIVCVerifiers: Number(BB_NUM_IVC_VERIFIERS), + bbIVCConcurrency: Number(BB_IVC_CONCURRENCY), + bbChonkVerifyMaxBatch: Number(BB_CHONK_VERIFY_MAX_BATCH), + bbChonkVerifyConcurrency: Number(BB_CHONK_VERIFY_BATCH_CONCURRENCY), }; } catch (err) { logger.error(`Native BB not available, error: ${err}`); diff --git a/yarn-project/end-to-end/src/fixtures/setup.ts b/yarn-project/end-to-end/src/fixtures/setup.ts index 059c7c295899..5352d81ec8ca 100644 --- a/yarn-project/end-to-end/src/fixtures/setup.ts +++ b/yarn-project/end-to-end/src/fixtures/setup.ts @@ -302,6 +302,8 @@ export async function setup( config.dataDirectory = directoryToCleanup; } + const dateProvider = new TestDateProvider(); + if (!config.l1RpcUrls?.length) { if (!isAnvilTestChain(chain.id)) { throw new Error(`No ETHEREUM_HOSTS set but non anvil chain requested`); @@ -311,6 +313,7 @@ export async function setup( accounts: opts.anvilAccounts, port: opts.anvilPort ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : undefined), slotsInAnEpoch: opts.anvilSlotsInAnEpoch, + dateProvider, }); anvil = res.anvil; config.l1RpcUrls = [res.rpcUrl]; @@ -322,8 +325,6 @@ export async function setup( logger.info(`Logging metrics to ${filename}`); setupMetricsLogger(filename); } - - const dateProvider = new TestDateProvider(); const ethCheatCodes = new EthCheatCodesWithState(config.l1RpcUrls, dateProvider); if (opts.stateLoad) { @@ -419,11 +420,12 @@ export async function setup( await ethCheatCodes.setIntervalMining(config.ethereumSlotDuration); } - // Always sync dateProvider to L1 time after deploying L1 contracts, regardless of mining mode. - // In compose mode, L1 time may have drifted ahead of system time due to the local-network watcher - // warping time forward on each filled slot. Without this sync, the sequencer computes the wrong - // slot from its dateProvider and cannot propose blocks. - dateProvider.setTime((await ethCheatCodes.timestamp()) * 1000); + // In compose mode (no local anvil), sync dateProvider to L1 time since it may have drifted + // ahead of system time due to the local-network watcher warping time forward on each filled slot. + // When running with a local anvil, the dateProvider is kept in sync via the stdout listener. + if (!anvil) { + dateProvider.setTime((await ethCheatCodes.lastBlockTimestamp()) * 1000); + } if (opts.l2StartTime) { await ethCheatCodes.warp(opts.l2StartTime, { resetBlockInterval: true }); diff --git a/yarn-project/end-to-end/src/simulators/lending_simulator.ts b/yarn-project/end-to-end/src/simulators/lending_simulator.ts index 404bb3d5ad8d..ae299b31e249 100644 --- a/yarn-project/end-to-end/src/simulators/lending_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/lending_simulator.ts @@ -94,7 +94,9 @@ export class LendingSimulator { async prepare() { this.accumulator = BASE; - const slot = await this.rollup.getSlotAt(BigInt(await this.cc.eth.timestamp()) + BigInt(this.ethereumSlotDuration)); + const slot = await this.rollup.getSlotAt( + BigInt(await this.cc.eth.lastBlockTimestamp()) + BigInt(this.ethereumSlotDuration), + ); this.time = Number(await this.rollup.getTimestampForSlot(slot)); } @@ -103,7 +105,7 @@ export class LendingSimulator { return; } - const slot = await this.rollup.getSlotAt(BigInt(await this.cc.eth.timestamp())); + const slot = await this.rollup.getSlotAt(BigInt(await this.cc.eth.lastBlockTimestamp())); const targetSlot = SlotNumber(slot + diff); const ts = Number(await this.rollup.getTimestampForSlot(targetSlot)); const timeDiff = ts - this.time; diff --git a/yarn-project/epoch-cache/src/epoch_cache.ts b/yarn-project/epoch-cache/src/epoch_cache.ts index 3ecb033c1f02..e91985647b14 100644 --- a/yarn-project/epoch-cache/src/epoch_cache.ts +++ b/yarn-project/epoch-cache/src/epoch_cache.ts @@ -9,6 +9,7 @@ import { type L1RollupConstants, getEpochAtSlot, getEpochNumberAtTimestamp, + getNextL1SlotTimestamp, getSlotAtTimestamp, getSlotRangeForEpoch, getTimestampForSlot, @@ -191,18 +192,14 @@ export class EpochCache implements EpochCacheInterface { return { ...this.getEpochAndSlotAtTimestamp(nowSeconds), nowMs }; } - public nowInSeconds(): bigint { - return BigInt(Math.floor(this.dateProvider.now() / 1000)); - } - private getEpochAndSlotAtSlot(slot: SlotNumber): EpochAndSlot { return this.getEpochAndSlotAtTimestamp(getTimestampForSlot(slot, this.l1constants)); } public getEpochAndSlotInNextL1Slot(): EpochAndSlot & { nowSeconds: bigint } { - const nowSeconds = this.nowInSeconds(); - const nextSlotTs = nowSeconds + BigInt(this.l1constants.ethereumSlotDuration); - return { ...this.getEpochAndSlotAtTimestamp(nextSlotTs), nowSeconds }; + const nowSeconds = this.dateProvider.nowInSeconds(); + const nextSlotTs = getNextL1SlotTimestamp(nowSeconds, this.l1constants); + return { ...this.getEpochAndSlotAtTimestamp(nextSlotTs), nowSeconds: BigInt(nowSeconds) }; } public getTargetEpochAndSlotInNextL1Slot(): EpochAndSlot & { nowSeconds: bigint } { @@ -440,10 +437,11 @@ export class EpochCache implements EpochCacheInterface { async getRegisteredValidators(): Promise { const validatorRefreshIntervalMs = this.config.validatorRefreshIntervalSeconds * 1000; const validatorRefreshTime = this.lastValidatorRefresh + validatorRefreshIntervalMs; - if (validatorRefreshTime < this.dateProvider.now()) { - const currentSet = await this.rollup.getAttesters(); + const now = this.dateProvider.now(); + if (validatorRefreshTime < now) { + const currentSet = await this.rollup.getAttesters(BigInt(Math.floor(now / 1000))); this.allValidators = new Set(currentSet.map(v => v.toString())); - this.lastValidatorRefresh = this.dateProvider.now(); + this.lastValidatorRefresh = now; } return Array.from(this.allValidators.keys()).map(v => EthAddress.fromString(v)); } diff --git a/yarn-project/ethereum/src/contracts/multicall.ts b/yarn-project/ethereum/src/contracts/multicall.ts index c79243b80b1b..40e17970e5db 100644 --- a/yarn-project/ethereum/src/contracts/multicall.ts +++ b/yarn-project/ethereum/src/contracts/multicall.ts @@ -2,7 +2,7 @@ import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer'; import { TimeoutError } from '@aztec/foundation/error'; import type { Logger } from '@aztec/foundation/log'; -import { type EncodeFunctionDataParameters, type Hex, encodeFunctionData, multicall3Abi } from 'viem'; +import { type Address, type EncodeFunctionDataParameters, type Hex, encodeFunctionData, multicall3Abi } from 'viem'; import type { L1BlobInputs, L1TxConfig, L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js'; import type { ExtendedViemWalletClient } from '../types.js'; @@ -11,6 +11,39 @@ import { RollupContract } from './rollup.js'; export const MULTI_CALL_3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11' as const; +/** ABI fragment for aggregate3Value — not included in viem's multicall3Abi. */ +export const aggregate3ValueAbi = [ + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'target', type: 'address' }, + { internalType: 'bool', name: 'allowFailure', type: 'bool' }, + { internalType: 'uint256', name: 'value', type: 'uint256' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' }, + ], + internalType: 'struct Multicall3.Call3Value[]', + name: 'calls', + type: 'tuple[]', + }, + ], + name: 'aggregate3Value', + outputs: [ + { + components: [ + { internalType: 'bool', name: 'success', type: 'bool' }, + { internalType: 'bytes', name: 'returnData', type: 'bytes' }, + ], + internalType: 'struct Multicall3.Result[]', + name: 'returnData', + type: 'tuple[]', + }, + ], + stateMutability: 'payable', + type: 'function', + }, +] as const; + export class Multicall3 { static async forward( requests: L1TxRequest[], @@ -122,6 +155,37 @@ export class Multicall3 { throw err; } } + + /** Batch multiple value transfers into a single aggregate3Value call on Multicall3. */ + static async forwardValue(calls: { to: Address; value: bigint }[], l1TxUtils: L1TxUtils, logger: Logger) { + const args = calls.map(c => ({ + target: c.to, + allowFailure: false, + value: c.value, + callData: '0x' as Hex, + })); + + const data = encodeFunctionData({ + abi: aggregate3ValueAbi, + functionName: 'aggregate3Value', + args: [args], + }); + + const totalValue = calls.reduce((sum, c) => sum + c.value, 0n); + + logger.info(`Sending aggregate3Value with ${calls.length} calls`, { totalValue }); + const { receipt } = await l1TxUtils.sendAndMonitorTransaction({ + to: MULTI_CALL_3_ADDRESS, + data, + value: totalValue, + }); + + if (receipt.status !== 'success') { + throw new Error(`aggregate3Value transaction reverted: ${receipt.transactionHash}`); + } + + return { receipt }; + } } export async function deployMulticall3(l1Client: ExtendedViemWalletClient, logger: Logger) { diff --git a/yarn-project/ethereum/src/contracts/rollup.ts b/yarn-project/ethereum/src/contracts/rollup.ts index 96d1c553ee1b..4c92882f1f16 100644 --- a/yarn-project/ethereum/src/contracts/rollup.ts +++ b/yarn-project/ethereum/src/contracts/rollup.ts @@ -781,12 +781,10 @@ export class RollupContract { public async canProposeAt( archive: Buffer, account: `0x${string}` | Account, - slotDuration: bigint, - slotOffset: bigint, + timestamp: bigint, opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {}, ): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> { - const latestBlock = await this.client.getBlock(); - const timeOfNextL1Slot = latestBlock.timestamp + slotDuration + slotOffset; + const timeOfNextL1Slot = timestamp; const who = typeof account === 'string' ? account : account.address; try { @@ -939,11 +937,10 @@ export class RollupContract { return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]); } - async getAttesters(): Promise { + async getAttesters(timestamp?: bigint): Promise { const attesterSize = await this.getActiveAttesterCount(); const gse = new GSEContract(this.client, await this.getGSE()); - const ts = (await this.client.getBlock()).timestamp; - + const ts = timestamp ?? (await this.client.getBlock()).timestamp; const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i)); const chunks = chunk(indices, 1000); diff --git a/yarn-project/ethereum/src/publisher_manager.test.ts b/yarn-project/ethereum/src/publisher_manager.test.ts index 3eb0141f824e..834f517dfe45 100644 --- a/yarn-project/ethereum/src/publisher_manager.test.ts +++ b/yarn-project/ethereum/src/publisher_manager.test.ts @@ -2,10 +2,28 @@ import { times } from '@aztec/foundation/collection'; import { EthAddress } from '@aztec/foundation/eth-address'; import { jest } from '@jest/globals'; +import { type Hex, encodeFunctionData } from 'viem'; +import { MULTI_CALL_3_ADDRESS, aggregate3ValueAbi } from './contracts/multicall.js'; import { L1TxUtils, TxUtilsState } from './l1_tx_utils/index.js'; import { PublisherManager } from './publisher_manager.js'; +/** Encode the expected aggregate3Value calldata for the given addresses and funding amount. */ +function expectedFundingData(addresses: EthAddress[], fundingAmount: bigint): Hex { + return encodeFunctionData({ + abi: aggregate3ValueAbi, + functionName: 'aggregate3Value', + args: [ + addresses.map(addr => ({ + target: addr.toString() as `0x${string}`, + allowFailure: false, + value: fundingAmount, + callData: '0x' as Hex, + })), + ], + }); +} + describe('PublisherManager', () => { let mockPublishers: (TestL1TxUtils & L1TxUtils)[]; let publisherManager: PublisherManager; @@ -14,6 +32,10 @@ describe('PublisherManager', () => { jest.clearAllMocks(); }); + afterEach(async () => { + await publisherManager?.stop(); + }); + describe('constructor', () => { it('should initialize with publishers', () => { mockPublishers = createMockPublishers(3); @@ -172,6 +194,206 @@ describe('PublisherManager', () => { }); }); + describe('publisher funding', () => { + let funder: TestL1TxUtils & L1TxUtils; + const threshold = 100n; + const fundingAmount = 50n; + + const createFundedManager = ( + publishers: (TestL1TxUtils & L1TxUtils)[], + funderInstance?: TestL1TxUtils & L1TxUtils, + config?: { publisherFundingThreshold?: bigint; publisherFundingAmount?: bigint }, + ) => { + return new PublisherManager( + publishers, + { publisherFundingThreshold: threshold, publisherFundingAmount: fundingAmount, ...config }, + { funder: funderInstance }, + ); + }; + + /** Start the manager and trigger one funding cycle via the RunningPromise. */ + const triggerFunding = async (manager: PublisherManager) => { + await manager.start(); + // RunningPromise calls the fn immediately on start, so we just need to wait for it to settle + await new Promise(resolve => setTimeout(resolve, 10)); + await manager.stop(); + }; + + beforeEach(() => { + funder = new TestL1TxUtils(EthAddress.random()) as TestL1TxUtils & L1TxUtils; + funder.balance = 5000n; + }); + + it('funds publisher when balance is below threshold', async () => { + mockPublishers = createMockPublishers(1); + mockPublishers[0].balance = 50n; // below threshold + publisherManager = createFundedManager(mockPublishers, funder); + + await triggerFunding(publisherManager); + + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledTimes(1); + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledWith({ + to: MULTI_CALL_3_ADDRESS, + data: expectedFundingData([mockPublishers[0].getSenderAddress()], fundingAmount), + value: fundingAmount, + }); + }); + + it('does not fund when publisher balance is above threshold', async () => { + mockPublishers = createMockPublishers(1); + mockPublishers[0].balance = 200n; // above threshold + publisherManager = createFundedManager(mockPublishers, funder); + + await triggerFunding(publisherManager); + + expect(funder.sendAndMonitorTransaction).not.toHaveBeenCalled(); + }); + + it('funds multiple publishers, only those below threshold', async () => { + mockPublishers = createMockPublishers(3); + mockPublishers[0].balance = 50n; // below + mockPublishers[1].balance = 200n; // above + mockPublishers[2].balance = 30n; // below + publisherManager = createFundedManager(mockPublishers, funder); + + await triggerFunding(publisherManager); + + // Single multicall for both underfunded publishers + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledTimes(1); + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledWith({ + to: MULTI_CALL_3_ADDRESS, + data: expectedFundingData( + [mockPublishers[0].getSenderAddress(), mockPublishers[2].getSenderAddress()], + fundingAmount, + ), + value: 2n * fundingAmount, + }); + }); + + it('correctly sends the funding transaction', async () => { + mockPublishers = createMockPublishers(1); + mockPublishers[0].balance = 50n; + publisherManager = createFundedManager(mockPublishers, funder); + + await triggerFunding(publisherManager); + + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledWith({ + to: MULTI_CALL_3_ADDRESS, + data: expectedFundingData([mockPublishers[0].getSenderAddress()], fundingAmount), + value: fundingAmount, + }); + }); + + it('handles funding transaction failure gracefully', async () => { + mockPublishers = createMockPublishers(2); + mockPublishers[0].balance = 50n; + mockPublishers[1].balance = 50n; + publisherManager = createFundedManager(mockPublishers, funder); + + funder.sendAndMonitorTransaction.mockRejectedValueOnce(new Error('tx failed')); + + await triggerFunding(publisherManager); + + // Single multicall attempted and failed — error caught by RunningPromise + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledTimes(1); + }); + + it('no funding triggered when no funder configured', async () => { + mockPublishers = createMockPublishers(1); + mockPublishers[0].balance = 50n; + publisherManager = new PublisherManager(mockPublishers, { + publisherFundingThreshold: threshold, + publisherFundingAmount: fundingAmount, + }); + + await triggerFunding(publisherManager); + + expect(funder.sendAndMonitorTransaction).not.toHaveBeenCalled(); + }); + + it('no funding triggered when config threshold/amount not set', async () => { + mockPublishers = createMockPublishers(1); + mockPublishers[0].balance = 50n; + publisherManager = createFundedManager(mockPublishers, funder, { + publisherFundingThreshold: undefined, + publisherFundingAmount: undefined, + }); + + await triggerFunding(publisherManager); + + expect(funder.sendAndMonitorTransaction).not.toHaveBeenCalled(); + }); + + it('does not fund when funder balance is less than fundingAmount', async () => { + mockPublishers = createMockPublishers(1); + mockPublishers[0].balance = 50n; + funder.balance = 30n; // less than fundingAmount (50n) + publisherManager = createFundedManager(mockPublishers, funder); + + await triggerFunding(publisherManager); + + expect(funder.sendAndMonitorTransaction).not.toHaveBeenCalled(); + }); + + it('caps funding to affordable number of publishers', async () => { + mockPublishers = createMockPublishers(3); + mockPublishers[0].balance = 10n; + mockPublishers[1].balance = 10n; + mockPublishers[2].balance = 10n; + funder.balance = 2n * fundingAmount; // enough for 2, not 3 + publisherManager = createFundedManager(mockPublishers, funder); + + await triggerFunding(publisherManager); + + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledTimes(1); + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledWith({ + to: MULTI_CALL_3_ADDRESS, + data: expectedFundingData( + [mockPublishers[0].getSenderAddress(), mockPublishers[1].getSenderAddress()], + fundingAmount, + ), + value: 2n * fundingAmount, + }); + }); + + it('disables funding when funder address matches a publisher', async () => { + const sharedAddress = EthAddress.random(); + mockPublishers = createMockPublishers(2, [sharedAddress]); + mockPublishers[0].balance = 50n; // same address as funder + mockPublishers[1].balance = 50n; // different address, also below threshold + funder = new TestL1TxUtils(sharedAddress) as TestL1TxUtils & L1TxUtils; + funder.balance = 5000n; + publisherManager = createFundedManager(mockPublishers, funder); + + await triggerFunding(publisherManager); + + // Funding is fully disabled because funder overlaps with a publisher + expect(funder.sendAndMonitorTransaction).not.toHaveBeenCalled(); + }); + + it('funds publishers in busy states', async () => { + mockPublishers = createMockPublishers(2); + mockPublishers[0].balance = 50n; + mockPublishers[0].state = TxUtilsState.IDLE; + mockPublishers[1].balance = 50n; + mockPublishers[1].state = TxUtilsState.SENT; // busy + publisherManager = createFundedManager(mockPublishers, funder); + + await triggerFunding(publisherManager); + + // Single multicall funds both, even the busy one + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledTimes(1); + expect(funder.sendAndMonitorTransaction).toHaveBeenCalledWith({ + to: MULTI_CALL_3_ADDRESS, + data: expectedFundingData( + [mockPublishers[0].getSenderAddress(), mockPublishers[1].getSenderAddress()], + fundingAmount, + ), + value: 2n * fundingAmount, + }); + }); + }); + function createMockPublishers(count: number, addresses: EthAddress[] = []): (TestL1TxUtils & L1TxUtils)[] { const tempAddress = [...addresses]; return times( @@ -185,6 +407,10 @@ class TestL1TxUtils { public state: TxUtilsState = TxUtilsState.IDLE; public lastMinedAtBlockNumber: bigint | undefined = undefined; public balance: bigint = 1000n; + public sendAndMonitorTransaction = jest.fn<() => Promise>().mockResolvedValue({ + receipt: { transactionHash: '0xabc', status: 'success' }, + state: {}, + }); constructor(public senderAddress: EthAddress) {} @@ -195,4 +421,8 @@ class TestL1TxUtils { public getSenderAddress() { return this.senderAddress; } + + public async loadStateAndResumeMonitoring() {} + + public interrupt() {} } diff --git a/yarn-project/ethereum/src/publisher_manager.ts b/yarn-project/ethereum/src/publisher_manager.ts index 3c620c37ec93..11149b7b0465 100644 --- a/yarn-project/ethereum/src/publisher_manager.ts +++ b/yarn-project/ethereum/src/publisher_manager.ts @@ -1,6 +1,8 @@ import { pick } from '@aztec/foundation/collection'; import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log'; +import { RunningPromise } from '@aztec/foundation/running-promise'; +import { Multicall3 } from './contracts/multicall.js'; import { L1TxUtils, TxUtilsState } from './l1_tx_utils/index.js'; // Defines the order in which we prioritise publishers based on their state (first is better) @@ -27,24 +29,72 @@ const busyStates: TxUtilsState[] = [ export type PublisherFilter = (utils: UtilsType) => boolean; +/** Config accepted by PublisherManager. */ +type PublisherManagerConfig = { + publisherAllowInvalidStates?: boolean; + publisherFundingThreshold?: bigint; + publisherFundingAmount?: bigint; +}; + export class PublisherManager { private log: Logger; - private config: { publisherAllowInvalidStates?: boolean }; + private config: PublisherManagerConfig; + private static readonly FUNDING_CHECK_INTERVAL_MS = 2 * 60 * 1000; + private funder?: UtilsType; + private fundingPromise?: RunningPromise; constructor( private publishers: UtilsType[], - config: { publisherAllowInvalidStates?: boolean }, - bindings?: LoggerBindings, + config: PublisherManagerConfig, + opts?: { bindings?: LoggerBindings; funder?: UtilsType }, ) { - this.log = createLogger('publisher:manager', bindings); + this.funder = opts?.funder; + this.log = createLogger('publisher:manager', opts?.bindings); this.log.info(`PublisherManager initialized with ${publishers.length} publishers.`); this.publishers = publishers; - this.config = pick(config, 'publisherAllowInvalidStates'); + this.config = pick(config, 'publisherAllowInvalidStates', 'publisherFundingThreshold', 'publisherFundingAmount'); + + const hasThreshold = this.config.publisherFundingThreshold !== undefined; + const hasAmount = this.config.publisherFundingAmount !== undefined; + if (hasThreshold !== hasAmount) { + this.log.warn(`Incomplete funding config: both publisherFundingThreshold and publisherFundingAmount must be set`); + } + + if (this.funder) { + const funderAddress = this.funder.getSenderAddress(); + if (publishers.some(p => p.getSenderAddress().equals(funderAddress))) { + this.log.error(`Funding account ${funderAddress} is also a publisher, disabling funding to avoid self-funding`); + this.funder = undefined; + } + } } - /** Loads the state of all publishers and resumes monitoring any pending txs */ - public async loadState(): Promise { - await Promise.all(this.publishers.map(pub => pub.loadStateAndResumeMonitoring())); + /** Loads the state of all publishers and the funder, and starts periodic funding checks. */ + public async start(): Promise { + await Promise.all([ + ...this.publishers.map(pub => pub.loadStateAndResumeMonitoring()), + this.funder?.loadStateAndResumeMonitoring(), + ]); + + if ( + this.funder && + this.config.publisherFundingThreshold !== undefined && + this.config.publisherFundingAmount !== undefined + ) { + this.fundingPromise = new RunningPromise( + () => this.triggerFundingIfNeeded(), + this.log, + PublisherManager.FUNDING_CHECK_INTERVAL_MS, + ); + this.fundingPromise.start(); + } + } + + /** Stops the funding loop and interrupts all publishers. */ + public async stop(): Promise { + await this.fundingPromise?.stop(); + this.publishers.forEach(pub => pub.interrupt()); + this.funder?.interrupt(); } // Finds and prioritises available publishers based on @@ -102,7 +152,52 @@ export class PublisherManager { return sortedPublishers[0].publisher; } - public interrupt() { - this.publishers.forEach(pub => pub.interrupt()); + /** Check all publisher balances and fund those below threshold. */ + private async triggerFundingIfNeeded(): Promise { + const { funder, config } = this; + if (!funder || config.publisherFundingThreshold === undefined || config.publisherFundingAmount === undefined) { + return; + } + + const allBalances = await Promise.all( + this.publishers.map(async pub => ({ balance: await pub.getSenderBalance(), publisher: pub })), + ); + const lowBalance = allBalances.filter(p => p.balance < config.publisherFundingThreshold!); + if (lowBalance.length === 0) { + return; + } + + const fundingAmount = config.publisherFundingAmount!; + const funderBalance = await funder.getSenderBalance(); + + if (funderBalance < 10n * fundingAmount) { + this.log.warn(`Funding account balance is low`, { funderBalance, threshold: 10n * fundingAmount }); + } + const affordableCount = Number(funderBalance / fundingAmount); + if (affordableCount === 0) { + this.log.error(`Funding account balance too low to fund any publisher`, { funderBalance, fundingAmount }); + return; + } + if (affordableCount < lowBalance.length) { + this.log.warn(`Funder can only afford ${affordableCount}/${lowBalance.length} publishers`, { + funderBalance, + fundingAmount, + }); + } + + const toFund = lowBalance.slice(0, affordableCount).map(p => p.publisher); + await this.fundPublishers(toFund); + } + + /** Fund publishers via a single Multicall3 aggregate3Value transaction. */ + private async fundPublishers(publishers: UtilsType[]): Promise { + const fundingAmount = this.config.publisherFundingAmount!; + const calls = publishers.map(pub => ({ + to: pub.getSenderAddress().toString(), + value: fundingAmount, + })); + + await Multicall3.forwardValue(calls, this.funder!, this.log); + this.log.info(`Funded ${publishers.length} publishers`); } } diff --git a/yarn-project/ethereum/src/test/eth_cheat_codes.ts b/yarn-project/ethereum/src/test/eth_cheat_codes.ts index d62b194c0241..b1cbdd199d6f 100644 --- a/yarn-project/ethereum/src/test/eth_cheat_codes.ts +++ b/yarn-project/ethereum/src/test/eth_cheat_codes.ts @@ -85,10 +85,12 @@ export class EthCheatCodes { } /** - * Get the current timestamp - * @returns The current timestamp + * Get the timestamp of the latest mined L1 block. + * Note: this is NOT the current time — it's the discrete timestamp of the last block. + * Between blocks, the actual chain time advances but no new block reflects it. + * @returns The latest block timestamp in seconds */ - public async timestamp(): Promise { + public async lastBlockTimestamp(): Promise { const res = await this.doRpcCall('eth_getBlockByNumber', ['latest', true]); return parseInt(res.timestamp, 16); } @@ -552,7 +554,7 @@ export class EthCheatCodes { } public async syncDateProvider() { - const timestamp = await this.timestamp(); + const timestamp = await this.lastBlockTimestamp(); if ('setTime' in this.dateProvider) { this.dateProvider.setTime(timestamp * 1000); } diff --git a/yarn-project/ethereum/src/test/start_anvil.test.ts b/yarn-project/ethereum/src/test/start_anvil.test.ts index 7e3b899ef7f6..ebd0fd80a304 100644 --- a/yarn-project/ethereum/src/test/start_anvil.test.ts +++ b/yarn-project/ethereum/src/test/start_anvil.test.ts @@ -1,5 +1,6 @@ import { type Logger, createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; +import { TestDateProvider } from '@aztec/foundation/timer'; import { createPublicClient, http, parseAbiItem } from 'viem'; @@ -52,4 +53,40 @@ describe('start_anvil', () => { stopWatching(); await sleep(100); }); + + it('syncs dateProvider to anvil block time on each mined block', async () => { + // Stop the default anvil instance (no dateProvider). + await anvil.stop(); + + const dateProvider = new TestDateProvider(); + const res = await startAnvil({ dateProvider }); + anvil = res.anvil; + rpcUrl = res.rpcUrl; + + const publicClient = createPublicClient({ transport: http(rpcUrl, { batch: false }) }); + + // Mine a block so anvil emits a "Block Time" line. + await publicClient.request({ method: 'evm_mine', params: [] } as any); + // Give the stdout listener time to fire. + await sleep(200); + + const block = await publicClient.getBlock({ blockTag: 'latest' }); + const blockTimeMs = Number(block.timestamp) * 1000; + // The dateProvider should now be within 2 seconds of the anvil block time. + // TestDateProvider.now() = Date.now() + offset, and setTime sets offset = blockTimeMs - Date.now(), + // so subsequent now() calls return blockTimeMs + elapsed. We check the difference is small. + expect(Math.abs(dateProvider.now() - blockTimeMs)).toBeLessThan(2000); + + // Warp anvil forward by 1000 seconds and verify the dateProvider follows. + const futureTimestamp = Number(block.timestamp) + 1000; + await publicClient.request({ + method: 'evm_setNextBlockTimestamp', + params: [futureTimestamp], + } as any); + await publicClient.request({ method: 'evm_mine', params: [] } as any); + await sleep(200); + + const futureTimeMs = futureTimestamp * 1000; + expect(Math.abs(dateProvider.now() - futureTimeMs)).toBeLessThan(2000); + }); }); diff --git a/yarn-project/ethereum/src/test/start_anvil.ts b/yarn-project/ethereum/src/test/start_anvil.ts index ba2f8b573604..f5ba8609e15f 100644 --- a/yarn-project/ethereum/src/test/start_anvil.ts +++ b/yarn-project/ethereum/src/test/start_anvil.ts @@ -1,5 +1,6 @@ import { createLogger } from '@aztec/foundation/log'; import { makeBackoff, retry } from '@aztec/foundation/retry'; +import type { TestDateProvider } from '@aztec/foundation/timer'; import { fileURLToPath } from '@aztec/foundation/url'; import { type ChildProcess, spawn } from 'child_process'; @@ -33,6 +34,12 @@ export async function startAnvil( * L1-finality-based logic work without needing hundreds of mined blocks. */ slotsInAnEpoch?: number; + /** + * If provided, the date provider will be synced to anvil's block time on every mined block. + * This keeps the dateProvider in lockstep with anvil's chain time, avoiding drift between + * the wall clock and the L1 chain when computing L1 slot timestamps. + */ + dateProvider?: TestDateProvider; } = {}, ): Promise<{ anvil: Anvil; methodCalls?: string[]; rpcUrl: string; stop: () => Promise }> { const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh'); @@ -108,12 +115,15 @@ export async function startAnvil( child.once('close', onClose); }); - // Continue piping for logging / method-call capture after startup. - if (logger || opts.captureMethodCalls) { + // Continue piping for logging, method-call capture, and/or dateProvider sync after startup. + if (logger || opts.captureMethodCalls || opts.dateProvider) { child.stdout?.on('data', (data: Buffer) => { const text = data.toString(); logger?.debug(text.trim()); methodCalls?.push(...(text.match(/eth_[^\s]+/g) || [])); + if (opts.dateProvider) { + syncDateProviderFromAnvilOutput(text, opts.dateProvider); + } }); child.stderr?.on('data', (data: Buffer) => { logger?.debug(data.toString().trim()); @@ -160,6 +170,19 @@ export async function startAnvil( return { anvil: anvilObj, methodCalls, stop, rpcUrl: `http://127.0.0.1:${port}` }; } +/** Extracts block time from anvil stdout and syncs the dateProvider. */ +function syncDateProviderFromAnvilOutput(text: string, dateProvider: TestDateProvider): void { + // Anvil logs mined blocks as: + // Block Time: "Fri, 20 Mar 2026 02:10:46 +0000" + const match = text.match(/Block Time:\s*"([^"]+)"/); + if (match) { + const blockTimeMs = new Date(match[1]).getTime(); + if (!isNaN(blockTimeMs)) { + dateProvider.setTime(blockTimeMs); + } + } +} + /** Send SIGTERM, wait up to 5 s, then SIGKILL. All timers are always cleared. */ function killChild(child: ChildProcess): Promise { return new Promise(resolve => { diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index 294288bc0cc4..d04a61dfcf8a 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -61,6 +61,7 @@ "./eth-address": "./dest/eth-address/index.js", "./eth-signature": "./dest/eth-signature/index.js", "./queue": "./dest/queue/index.js", + "./fifo": "./dest/fifo/index.js", "./fs": "./dest/fs/index.js", "./buffer": "./dest/buffer/index.js", "./json-rpc": "./dest/json-rpc/index.js", diff --git a/yarn-project/foundation/src/branded-types/slot.ts b/yarn-project/foundation/src/branded-types/slot.ts index 069104a88fe0..2657cd622036 100644 --- a/yarn-project/foundation/src/branded-types/slot.ts +++ b/yarn-project/foundation/src/branded-types/slot.ts @@ -73,6 +73,11 @@ SlotNumber.isValid = function (value: unknown): value is SlotNumber { return typeof value === 'number' && Number.isInteger(value) && value >= 0; }; +/** Increments a SlotNumber by a given value. */ +SlotNumber.add = function (sn: SlotNumber, increment: number): SlotNumber { + return SlotNumber(sn + increment); +}; + /** * The zero slot value. */ diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 9cc98c492272..761e2b8c7dc8 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -23,6 +23,8 @@ export type EnvVar = | 'BB_WORKING_DIRECTORY' | 'BB_NUM_IVC_VERIFIERS' | 'BB_IVC_CONCURRENCY' + | 'BB_CHONK_VERIFY_MAX_BATCH' + | 'BB_CHONK_VERIFY_BATCH_CONCURRENCY' | 'BOOTSTRAP_NODES' | 'BLOB_ARCHIVE_API_URL' | 'BLOB_FILE_STORE_URLS' @@ -219,6 +221,8 @@ export type EnvVar = | 'SEQ_PUBLISHER_ADDRESSES' | 'SEQ_PUBLISHER_ALLOW_INVALID_STATES' | 'SEQ_PUBLISHER_FORWARDER_ADDRESS' + | 'PUBLISHER_FUNDING_THRESHOLD' + | 'PUBLISHER_FUNDING_AMOUNT' | 'SEQ_POLLING_INTERVAL_MS' | 'SEQ_ENFORCE_TIME_TABLE' | 'SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT' diff --git a/yarn-project/foundation/src/fifo/fifo_frame_reader.test.ts b/yarn-project/foundation/src/fifo/fifo_frame_reader.test.ts new file mode 100644 index 000000000000..a8c710a9ed44 --- /dev/null +++ b/yarn-project/foundation/src/fifo/fifo_frame_reader.test.ts @@ -0,0 +1,209 @@ +import { PassThrough } from 'node:stream'; + +import { FifoFrameReader } from './fifo_frame_reader.js'; + +/** Build a length-delimited frame buffer for the given payload. */ +function buildFrame(payload: Buffer): Buffer { + const header = Buffer.alloc(4); + header.writeUInt32BE(payload.length, 0); + return Buffer.concat([header, payload]); +} + +/** Collect N frames from a reader, returning them as a promise. */ +function collectFrames(reader: FifoFrameReader, count: number): Promise { + return new Promise((resolve, reject) => { + const frames: Buffer[] = []; + reader.on('frame', frame => { + frames.push(frame); + if (frames.length >= count) { + resolve(frames); + } + }); + reader.on('error', reject); + }); +} + +describe('FifoFrameReader', () => { + it('reads a single frame', async () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + const framesPromise = collectFrames(reader, 1); + stream.write(buildFrame(Buffer.from('hello world'))); + stream.end(); + + const frames = await framesPromise; + expect(frames).toHaveLength(1); + expect(frames[0].toString()).toBe('hello world'); + reader.stop(); + }); + + it('reads multiple frames from a single push', async () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + const framesPromise = collectFrames(reader, 3); + + const combined = Buffer.concat([ + buildFrame(Buffer.from('aaa')), + buildFrame(Buffer.from('bbb')), + buildFrame(Buffer.from('ccc')), + ]); + stream.write(combined); + stream.end(); + + const frames = await framesPromise; + expect(frames).toHaveLength(3); + expect(frames.map(f => f.toString())).toEqual(['aaa', 'bbb', 'ccc']); + reader.stop(); + }); + + it('handles frames split across multiple chunks', async () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + const framesPromise = collectFrames(reader, 1); + + const payload = Buffer.from('this is a longer payload that will be split'); + const frame = buildFrame(payload); + + // Split the frame into small chunks + for (let i = 0; i < frame.length; i += 5) { + stream.write(frame.subarray(i, Math.min(i + 5, frame.length))); + } + stream.end(); + + const frames = await framesPromise; + expect(frames).toHaveLength(1); + expect(frames[0].toString()).toBe(payload.toString()); + reader.stop(); + }); + + it('handles header split across chunks', async () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + const framesPromise = collectFrames(reader, 1); + + const payload = Buffer.from('data'); + const frame = buildFrame(payload); + + // Split in the middle of the 4-byte header + stream.write(frame.subarray(0, 2)); + stream.write(frame.subarray(2)); + stream.end(); + + const frames = await framesPromise; + expect(frames).toHaveLength(1); + expect(frames[0].toString()).toBe('data'); + reader.stop(); + }); + + it('emits error on zero-length payload', async () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + const errorPromise = new Promise(resolve => { + reader.on('error', resolve); + }); + + const header = Buffer.alloc(4); + header.writeUInt32BE(0, 0); + stream.write(header); + + const error = await errorPromise; + expect(error.message).toContain('Invalid payload length: 0'); + }); + + it('emits error on oversized payload', async () => { + const maxSize = 100; + const reader = new FifoFrameReader(maxSize); + const stream = new PassThrough(); + reader.startFromStream(stream); + + const errorPromise = new Promise(resolve => { + reader.on('error', resolve); + }); + + const header = Buffer.alloc(4); + header.writeUInt32BE(maxSize + 1, 0); + stream.write(header); + + const error = await errorPromise; + expect(error.message).toContain(`Invalid payload length: ${maxSize + 1}`); + }); + + it('emits end when stream ends', async () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + const endPromise = new Promise(resolve => { + reader.on('end', resolve); + }); + + stream.end(); + await endPromise; + reader.stop(); + }); + + it('reads valid frames before an invalid frame', async () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + const frames: Buffer[] = []; + reader.on('frame', frame => frames.push(frame)); + + const errorPromise = new Promise(resolve => { + reader.on('error', resolve); + }); + + // Write 2 valid frames then an invalid one + stream.write(buildFrame(Buffer.from('good1'))); + stream.write(buildFrame(Buffer.from('good2'))); + + // Invalid: zero length header + const badHeader = Buffer.alloc(4); + badHeader.writeUInt32BE(0, 0); + stream.write(badHeader); + + await errorPromise; + expect(frames).toHaveLength(2); + expect(frames[0].toString()).toBe('good1'); + expect(frames[1].toString()).toBe('good2'); + }); + + it('throws if started twice', () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + expect(() => reader.startFromStream(new PassThrough())).toThrow('already running'); + reader.stop(); + }); + + it('handles large payloads', async () => { + const stream = new PassThrough(); + const reader = new FifoFrameReader(); + reader.startFromStream(stream); + + const framesPromise = collectFrames(reader, 1); + + // 1MB payload + const payload = Buffer.alloc(1024 * 1024, 0x42); + stream.write(buildFrame(payload)); + stream.end(); + + const frames = await framesPromise; + expect(frames).toHaveLength(1); + expect(frames[0].length).toBe(1024 * 1024); + expect(frames[0][0]).toBe(0x42); + reader.stop(); + }); +}); diff --git a/yarn-project/foundation/src/fifo/fifo_frame_reader.ts b/yarn-project/foundation/src/fifo/fifo_frame_reader.ts new file mode 100644 index 000000000000..3b3bd322d33b --- /dev/null +++ b/yarn-project/foundation/src/fifo/fifo_frame_reader.ts @@ -0,0 +1,98 @@ +import EventEmitter from 'node:events'; +import * as fs from 'node:fs'; +import type { Readable } from 'node:stream'; + +/** + * Events emitted by FifoFrameReader. + * + * - `frame`: A complete frame payload (without the 4-byte length header). + * - `error`: An unrecoverable error (invalid frame length, stream error). + * - `end`: The underlying stream has ended. + */ +export interface FifoFrameReaderEvents { + frame: [payload: Buffer]; + error: [error: Error]; + end: []; +} + +/** + * Reads length-delimited frames from a readable stream (typically a named FIFO pipe). + * + * Wire format: `[4-byte big-endian payload length][payload bytes]` + * + * Emits a `frame` event for each complete frame with the raw payload buffer. + * Callers are responsible for deserializing the payload (e.g., via msgpack). + * + * On encountering an invalid payload length (0 or >maxPayloadSize), emits `error` + * and destroys the stream. + */ +export class FifoFrameReader extends EventEmitter { + private stream: Readable | null = null; + private pendingBuf: Buffer = Buffer.alloc(0); + private running = false; + + constructor(private readonly maxPayloadSize = 10 * 1024 * 1024) { + super(); + } + + /** Open a FIFO at the given path and start reading frames. */ + start(fifoPath: string, highWaterMark = 64 * 1024): void { + this.startFromStream(fs.createReadStream(fifoPath, { highWaterMark })); + } + + /** Start reading frames from an existing readable stream. */ + startFromStream(stream: Readable): void { + if (this.running) { + throw new Error('FifoFrameReader is already running'); + } + this.running = true; + this.pendingBuf = Buffer.alloc(0); + this.stream = stream; + + stream.on('data', (chunk: Buffer | string) => { + const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); + this.pendingBuf = this.pendingBuf.length > 0 ? Buffer.concat([this.pendingBuf, buf]) : buf; + this.drainFrames(); + }); + + stream.on('error', (err: Error) => { + if (this.running) { + this.emit('error', err); + } + }); + + stream.on('end', () => { + this.emit('end'); + }); + } + + /** Stop reading and destroy the underlying stream. */ + stop(): void { + this.running = false; + if (this.stream) { + this.stream.destroy(); + this.stream = null; + } + } + + /** Parse complete frames out of the pending buffer. */ + private drainFrames(): void { + while (this.pendingBuf.length >= 4) { + const payloadLen = this.pendingBuf.readUInt32BE(0); + if (payloadLen === 0 || payloadLen > this.maxPayloadSize) { + this.emit('error', new Error(`Invalid payload length: ${payloadLen}`)); + this.stop(); + return; + } + + const frameLen = 4 + payloadLen; + if (this.pendingBuf.length < frameLen) { + break; // Wait for more data + } + + const payload = this.pendingBuf.subarray(4, frameLen); + this.pendingBuf = this.pendingBuf.subarray(frameLen); + this.emit('frame', Buffer.from(payload)); + } + } +} diff --git a/yarn-project/foundation/src/fifo/index.ts b/yarn-project/foundation/src/fifo/index.ts new file mode 100644 index 000000000000..d53ee629f835 --- /dev/null +++ b/yarn-project/foundation/src/fifo/index.ts @@ -0,0 +1 @@ +export { FifoFrameReader } from './fifo_frame_reader.js'; diff --git a/yarn-project/ivc-integration/src/batch_verifier.bench.test.ts b/yarn-project/ivc-integration/src/batch_verifier.bench.test.ts new file mode 100644 index 000000000000..8282aa50b54e --- /dev/null +++ b/yarn-project/ivc-integration/src/batch_verifier.bench.test.ts @@ -0,0 +1,195 @@ +/** + * Batch chonk verifier benchmarks using real protocol proofs. + * + * Uses pre-generated IVC inputs from example-app-ivc-inputs-out (built by + * end-to-end/bootstrap.sh build_bench), proves a representative transaction, + * then benchmarks batch verification throughput at various configurations. + */ +import { BatchChonkVerifier } from '@aztec/bb-prover'; +import { createLogger } from '@aztec/foundation/log'; +import { ProtocolCircuitVks } from '@aztec/noir-protocol-circuits-types/server/vks'; + +import { jest } from '@jest/globals'; +import { execFile } from 'node:child_process'; +import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { dirname, resolve } from 'node:path'; +import { promisify } from 'node:util'; + +import { corruptProofFields } from './batch_verifier_test_helpers.js'; + +const execFileAsync = promisify(execFile); +const logger = createLogger('ivc-integration:bench:batch-verifier'); + +const INPUTS_DIR = resolve('../end-to-end/example-app-ivc-inputs-out'); +const BB_PATH = process.env.BB_BINARY_PATH ?? resolve('../../barretenberg/cpp/build/bin/bb'); + +jest.setTimeout(1_200_000); // 20 min — proving is slow + +type BenchEntry = { name: string; value: number; unit: string }; + +/** Split a binary proof file into 32-byte field element buffers. */ +function proofToFields(proofBuf: Buffer): Uint8Array[] { + const fields: Uint8Array[] = []; + for (let i = 0; i < proofBuf.length; i += 32) { + fields.push(Uint8Array.from(proofBuf.subarray(i, i + 32))); + } + return fields; +} + +describe('Batch Chonk Verifier Benchmarks (Real Proofs)', () => { + let validProofFields: Uint8Array[]; + let invalidProofFields: Uint8Array[]; + let vk: Uint8Array; + let proofDir: string; + const benchResults: BenchEntry[] = []; + + beforeAll(async () => { + // Use pre-generated IVC inputs from example-app-ivc-inputs-out + logger.info(`Using local IVC inputs from ${INPUTS_DIR}...`); + + // Pick the largest flow for a realistic proof + const flows = await readdir(INPUTS_DIR, { withFileTypes: true }); + const flowDirs = flows.filter(f => f.isDirectory()).map(f => f.name); + logger.info(`Available flows: ${flowDirs.join(', ')}`); + + // Use a transfer flow (representative of typical txs) + const flow = flowDirs.find(f => f.includes('transfer_0_recursions+sponsored')) ?? flowDirs[0]; + const ivcInputsPath = resolve(INPUTS_DIR, flow, 'ivc-inputs.msgpack'); + proofDir = resolve(tmpdir(), `bb-bench-proof-${Date.now()}`); + await mkdir(proofDir, { recursive: true }); + + // Prove the flow + logger.info(`Proving flow: ${flow}...`); + const proveStart = performance.now(); + await execFileAsync(BB_PATH, ['prove', '--scheme', 'chonk', '--ivc_inputs_path', ivcInputsPath, '-o', proofDir], { + timeout: 600_000, + }); + const proveMs = performance.now() - proveStart; + logger.info(`Proof generated in ${Math.ceil(proveMs)}ms`); + + // Read the proof and convert to field elements + const proofBuf = await readFile(resolve(proofDir, 'proof')); + validProofFields = proofToFields(proofBuf); + invalidProofFields = corruptProofFields(validProofFields); + + // Get the protocol VK (HidingKernelToRollup — matches most flows) + vk = ProtocolCircuitVks['HidingKernelToRollup'].keyAsBytes; + + logger.info(`Proof: ${proofBuf.length} bytes, ${validProofFields.length} fields`); + }); + + afterAll(async () => { + if (process.env.BENCH_OUTPUT) { + await mkdir(dirname(process.env.BENCH_OUTPUT), { recursive: true }); + await writeFile(process.env.BENCH_OUTPUT, JSON.stringify(benchResults, null, 2)); + } else { + logger.info('Benchmark results:'); + for (const r of benchResults) { + logger.info(` ${r.name}: ${r.value.toFixed(1)} ${r.unit}`); + } + } + await rm(proofDir, { recursive: true, force: true }).catch(() => {}); + }); + + // -- Core count sweep -- + + for (const numCores of [2, 4, 8]) { + it(`throughput: 16 proofs, batch_size=8, ${numCores} cores`, async () => { + const numProofs = 16; + const batchSize = 8; + const proofs = Array.from({ length: numProofs }, () => ({ vkIndex: 0, proofFields: validProofFields })); + + const verifier = await BatchChonkVerifier.newForTesting({ bbChonkVerifyConcurrency: numCores }, [vk], batchSize); + + try { + const wallStart = performance.now(); + const results = await Promise.all(proofs.map(p => verifier.enqueueProof(p.vkIndex, p.proofFields))); + const wallMs = performance.now() - wallStart; + + expect(results).toHaveLength(numProofs); + expect(results.every(r => r.valid)).toBe(true); + + const avgVerifyMs = results.reduce((sum, r) => sum + r.durationMs, 0) / results.length; + const throughput = (numProofs / wallMs) * 1000; + + benchResults.push( + { name: `BatchVerify/16_real_proofs/${numCores}_cores/wall_time`, value: wallMs, unit: 'ms' }, + { name: `BatchVerify/16_real_proofs/${numCores}_cores/avg_verify`, value: avgVerifyMs, unit: 'ms' }, + { name: `BatchVerify/16_real_proofs/${numCores}_cores/throughput`, value: throughput, unit: 'proofs/sec' }, + ); + + logger.info(`16 proofs, ${numCores} cores`, { + wallMs: Math.ceil(wallMs), + avgVerifyMs: Math.ceil(avgVerifyMs), + throughput: throughput.toFixed(2), + }); + } finally { + await verifier.stop(); + } + }); + } + + // -- Batch size sweep -- + + for (const batchSize of [2, 4, 8]) { + it(`batch_size sweep: 8 proofs, batch_size=${batchSize}, 8 cores`, async () => { + const numProofs = 8; + const proofs = Array.from({ length: numProofs }, () => ({ vkIndex: 0, proofFields: validProofFields })); + + const verifier = await BatchChonkVerifier.newForTesting({ bbChonkVerifyConcurrency: 8 }, [vk], batchSize); + + try { + const wallStart = performance.now(); + const results = await Promise.all(proofs.map(p => verifier.enqueueProof(p.vkIndex, p.proofFields))); + const wallMs = performance.now() - wallStart; + + expect(results).toHaveLength(numProofs); + expect(results.every(r => r.valid)).toBe(true); + + const throughput = (numProofs / wallMs) * 1000; + + benchResults.push( + { name: `BatchVerify/batch_size_${batchSize}/wall_time`, value: wallMs, unit: 'ms' }, + { name: `BatchVerify/batch_size_${batchSize}/throughput`, value: throughput, unit: 'proofs/sec' }, + ); + + logger.info(`batch_size=${batchSize}`, { wallMs: Math.ceil(wallMs), throughput: throughput.toFixed(2) }); + } finally { + await verifier.stop(); + } + }); + } + + // -- Bisection overhead -- + + it('bisection overhead: 8 proofs with 2 bad, batch_size=8, 8 cores', async () => { + const numProofs = 8; + const numBad = 2; + const proofs = Array.from({ length: numProofs }, (_, i) => ({ + vkIndex: 0, + proofFields: i < numBad ? invalidProofFields : validProofFields, + })); + + const verifier = await BatchChonkVerifier.newForTesting({ bbChonkVerifyConcurrency: 8 }, [vk], 8); + + try { + const wallStart = performance.now(); + const results = await Promise.all(proofs.map(p => verifier.enqueueProof(p.vkIndex, p.proofFields))); + const wallMs = performance.now() - wallStart; + + expect(results).toHaveLength(numProofs); + expect(results.filter(r => r.valid)).toHaveLength(numProofs - numBad); + expect(results.filter(r => !r.valid)).toHaveLength(numBad); + + benchResults.push({ + name: `BatchVerify/mixed_${numBad}_bad_of_${numProofs}/wall_time`, + value: wallMs, + unit: 'ms', + }); + logger.info(`mixed ${numBad} bad of ${numProofs}`, { wallMs: Math.ceil(wallMs) }); + } finally { + await verifier.stop(); + } + }); +}); diff --git a/yarn-project/ivc-integration/src/batch_verifier.test.ts b/yarn-project/ivc-integration/src/batch_verifier.test.ts new file mode 100644 index 000000000000..66dd1ca18cca --- /dev/null +++ b/yarn-project/ivc-integration/src/batch_verifier.test.ts @@ -0,0 +1,105 @@ +import { AztecClientBackend, BackendType, Barretenberg } from '@aztec/bb.js'; +import { createLogger } from '@aztec/foundation/log'; + +import { jest } from '@jest/globals'; + +import { corruptProofFields, runBatchVerifier } from './batch_verifier_test_helpers.js'; +import { generateTestingIVCStack } from './witgen.js'; + +const logger = createLogger('ivc-integration:test:batch-verifier'); + +jest.setTimeout(300_000); + +describe('Batch Chonk Verifier workloads', () => { + let bb: Barretenberg; + let validProofFields: Uint8Array[]; + let invalidProofFields: Uint8Array[]; + let vk: Uint8Array; + let validProofFields2: Uint8Array[]; + let vk2: Uint8Array; + + beforeAll(async () => { + bb = await Barretenberg.new({ backend: BackendType.NativeUnixSocket }); + await bb.initSRSChonk(); + + logger.info('Generating simple proof...'); + const [bytecodes1, witnesses1, , vks1] = await generateTestingIVCStack(1, 0); + const backend1 = new AztecClientBackend(bytecodes1, bb); + const [proofFields1, , generatedVk1] = await backend1.prove(witnesses1, vks1); + validProofFields = proofFields1; + invalidProofFields = corruptProofFields(validProofFields); + vk = generatedVk1; + + logger.info('Generating complex proof...'); + const [bytecodes2, witnesses2, , vks2] = await generateTestingIVCStack(1, 1); + const backend2 = new AztecClientBackend(bytecodes2, bb); + const [proofFields2, , generatedVk2] = await backend2.prove(witnesses2, vks2); + validProofFields2 = proofFields2; + vk2 = generatedVk2; + + logger.info('Proofs generated, ready for batch tests'); + }); + + afterAll(async () => { + await bb.destroy(); + }); + + it('should flush a single proof without waiting for a full batch', async () => { + const results = await runBatchVerifier({ + vks: [vk], + numCores: 0, + batchSize: 4, + proofs: [{ vkIndex: 0, proofFields: validProofFields }], + }); + expect(results).toHaveLength(1); + expect(results[0].valid).toBe(true); + }); + + it('should verify multiple proofs in parallel', async () => { + const results = await runBatchVerifier({ + vks: [vk], + numCores: 0, + batchSize: 8, + proofs: Array.from({ length: 4 }, () => ({ vkIndex: 0, proofFields: validProofFields })), + }); + expect(results).toHaveLength(4); + expect(results.every(r => r.valid)).toBe(true); + }); + + it('should handle mixed valid and invalid proofs in one batch', async () => { + const results = await runBatchVerifier({ + vks: [vk], + numCores: 0, + batchSize: 8, + proofs: [ + { vkIndex: 0, proofFields: validProofFields }, + { vkIndex: 0, proofFields: invalidProofFields }, + { vkIndex: 0, proofFields: validProofFields }, + { vkIndex: 0, proofFields: invalidProofFields }, + { vkIndex: 0, proofFields: validProofFields }, + ], + }); + expect(results).toHaveLength(5); + expect(results[0].valid).toBe(true); + expect(results[1].valid).toBe(false); + expect(results[2].valid).toBe(true); + expect(results[3].valid).toBe(false); + expect(results[4].valid).toBe(true); + }); + + it('should verify proofs with multiple VKs', async () => { + const results = await runBatchVerifier({ + vks: [vk, vk2], + numCores: 0, + batchSize: 8, + proofs: [ + { vkIndex: 0, proofFields: validProofFields }, + { vkIndex: 1, proofFields: validProofFields2 }, + { vkIndex: 0, proofFields: validProofFields }, + { vkIndex: 1, proofFields: validProofFields2 }, + ], + }); + expect(results).toHaveLength(4); + expect(results.every(r => r.valid)).toBe(true); + }); +}); diff --git a/yarn-project/ivc-integration/src/batch_verifier_queue.test.ts b/yarn-project/ivc-integration/src/batch_verifier_queue.test.ts new file mode 100644 index 000000000000..466c2072512a --- /dev/null +++ b/yarn-project/ivc-integration/src/batch_verifier_queue.test.ts @@ -0,0 +1,257 @@ +/** + * Batch chonk verifier queue robustness tests. + * + * Exercises edge cases: sub-batch flush, single proof, degenerate batch sizes, + * all-invalid, mixed valid/invalid with bisection, sequential start/stop cycles, + * and core count extremes. + * + * All tests exercise the full BatchChonkVerifier TS class (not raw bb.js). + */ +import { AztecClientBackend, BackendType, Barretenberg } from '@aztec/bb.js'; +import { createLogger } from '@aztec/foundation/log'; + +import { jest } from '@jest/globals'; + +import { corruptProofFields, runBatchVerifier } from './batch_verifier_test_helpers.js'; +import { generateTestingIVCStack } from './witgen.js'; + +const logger = createLogger('ivc-integration:test:batch-verifier-queue'); + +jest.setTimeout(600_000); + +describe('Batch Chonk Verifier Queue', () => { + let bb: Barretenberg; + let validProofFields: Uint8Array[]; + let invalidProofFields: Uint8Array[]; + let vk: Uint8Array; + + beforeAll(async () => { + bb = await Barretenberg.new({ backend: BackendType.NativeUnixSocket }); + await bb.initSRSChonk(); + + logger.info('Generating proof for tests...'); + const [bytecodes, witnesses, , vks] = await generateTestingIVCStack(1, 0); + const backend = new AztecClientBackend(bytecodes, bb); + const [proofFields, , generatedVk] = await backend.prove(witnesses, vks); + validProofFields = proofFields; + invalidProofFields = corruptProofFields(validProofFields); + vk = generatedVk; + logger.info('Proof generated'); + }); + + afterAll(async () => { + await bb.destroy(); + }); + + /** Shorthand for runBatchVerifier with common config. */ + function run(opts: { + numCores?: number; + batchSize?: number; + proofs: { vkIndex: number; proofFields: Uint8Array[] }[]; + }) { + return runBatchVerifier({ + vks: [vk], + numCores: opts.numCores ?? 4, + batchSize: opts.batchSize ?? 8, + proofs: opts.proofs, + }); + } + + // -- Basic cases -- + + it('single valid proof', async () => { + const results = await run({ + proofs: [{ vkIndex: 0, proofFields: validProofFields }], + }); + expect(results).toHaveLength(1); + expect(results[0].valid).toBe(true); + }); + + it('single invalid proof', async () => { + const results = await run({ + proofs: [{ vkIndex: 0, proofFields: invalidProofFields }], + }); + expect(results).toHaveLength(1); + expect(results[0].valid).toBe(false); + }); + + // -- Sub-batch flush: N < batch_size -- + + for (const n of [1, 2, 3, 5, 7]) { + it(`flushes ${n} proof(s) with batch_size=8`, async () => { + const proofs = Array.from({ length: n }, () => ({ vkIndex: 0, proofFields: validProofFields })); + const results = await run({ proofs }); + expect(results).toHaveLength(n); + expect(results.every(r => r.valid)).toBe(true); + }); + } + + // -- Exact batch boundary -- + + it('N exactly equals batch_size', async () => { + const results = await run({ + batchSize: 4, + proofs: Array.from({ length: 4 }, () => ({ vkIndex: 0, proofFields: validProofFields })), + }); + expect(results).toHaveLength(4); + expect(results.every(r => r.valid)).toBe(true); + }); + + it('N is one more than batch_size', async () => { + const results = await run({ + batchSize: 4, + proofs: Array.from({ length: 5 }, () => ({ vkIndex: 0, proofFields: validProofFields })), + }); + expect(results).toHaveLength(5); + expect(results.every(r => r.valid)).toBe(true); + }); + + // -- Degenerate batch_size=1 (every proof is its own batch) -- + + it('batch_size=1 verifies each proof individually', async () => { + const results = await run({ + batchSize: 1, + proofs: [ + { vkIndex: 0, proofFields: validProofFields }, + { vkIndex: 0, proofFields: invalidProofFields }, + { vkIndex: 0, proofFields: validProofFields }, + { vkIndex: 0, proofFields: invalidProofFields }, + ], + }); + expect(results).toHaveLength(4); + expect(results[0].valid).toBe(true); + expect(results[1].valid).toBe(false); + expect(results[2].valid).toBe(true); + expect(results[3].valid).toBe(false); + }); + + // -- All invalid -- + + it('all proofs invalid', async () => { + const proofs = Array.from({ length: 8 }, () => ({ vkIndex: 0, proofFields: invalidProofFields })); + const results = await run({ batchSize: 4, proofs }); + expect(results).toHaveLength(8); + expect(results.every(r => !r.valid)).toBe(true); + }); + + // -- Mixed valid/invalid with bisection -- + + it('1 bad out of 8 (bisection identifies it)', async () => { + const proofs = Array.from({ length: 8 }, (_, i) => ({ + vkIndex: 0, + proofFields: i === 3 ? invalidProofFields : validProofFields, + })); + const results = await run({ batchSize: 8, proofs }); + expect(results).toHaveLength(8); + expect(results[3].valid).toBe(false); + expect(results.filter(r => r.valid)).toHaveLength(7); + }); + + it('bad proofs at batch boundaries', async () => { + const proofs = Array.from({ length: 8 }, (_, i) => ({ + vkIndex: 0, + proofFields: i === 0 || i === 4 ? invalidProofFields : validProofFields, + })); + const results = await run({ batchSize: 4, proofs }); + expect(results).toHaveLength(8); + expect(results[0].valid).toBe(false); + expect(results[4].valid).toBe(false); + expect(results.filter(r => r.valid)).toHaveLength(6); + }); + + it('half bad proofs', async () => { + const proofs = Array.from({ length: 16 }, (_, i) => ({ + vkIndex: 0, + proofFields: i % 2 === 0 ? invalidProofFields : validProofFields, + })); + const results = await run({ numCores: 8, batchSize: 8, proofs }); + expect(results).toHaveLength(16); + expect(results.filter(r => r.valid)).toHaveLength(8); + expect(results.filter(r => !r.valid)).toHaveLength(8); + }); + + // -- Core count extremes -- + + it('works with numCores=1', async () => { + const proofs = Array.from({ length: 4 }, () => ({ vkIndex: 0, proofFields: validProofFields })); + const results = await run({ numCores: 1, batchSize: 4, proofs }); + expect(results).toHaveLength(4); + expect(results.every(r => r.valid)).toBe(true); + }); + + it('16 cores, batch_size=16, 32 proofs', async () => { + const proofs = Array.from({ length: 32 }, () => ({ vkIndex: 0, proofFields: validProofFields })); + const results = await run({ numCores: 16, batchSize: 16, proofs }); + expect(results).toHaveLength(32); + expect(results.every(r => r.valid)).toBe(true); + }); + + // -- Sequential start/stop cycles -- + + it('can start, verify, stop, then start again', async () => { + const results1 = await run({ + batchSize: 4, + proofs: Array.from({ length: 4 }, () => ({ vkIndex: 0, proofFields: validProofFields })), + }); + expect(results1).toHaveLength(4); + expect(results1.every(r => r.valid)).toBe(true); + + const results2 = await run({ + numCores: 8, + batchSize: 2, + proofs: [ + { vkIndex: 0, proofFields: validProofFields }, + { vkIndex: 0, proofFields: invalidProofFields }, + { vkIndex: 0, proofFields: validProofFields }, + ], + }); + expect(results2).toHaveLength(3); + expect(results2[0].valid).toBe(true); + expect(results2[1].valid).toBe(false); + expect(results2[2].valid).toBe(true); + }); + + // -- Random bisection patterns -- + + /** Simple seeded PRNG for deterministic randomness in tests. */ + function seededRandom(seed: number): () => number { + let s = seed; + return () => { + s = (s * 1664525 + 1013904223) & 0xffffffff; + return (s >>> 0) / 0xffffffff; + }; + } + + for (const { totalProofs, batchSize, numBad, seed } of [ + { totalProofs: 8, batchSize: 8, numBad: 1, seed: 42 }, + { totalProofs: 8, batchSize: 8, numBad: 3, seed: 123 }, + { totalProofs: 8, batchSize: 4, numBad: 2, seed: 999 }, + { totalProofs: 12, batchSize: 4, numBad: 4, seed: 7 }, + { totalProofs: 16, batchSize: 8, numBad: 5, seed: 314 }, + { totalProofs: 6, batchSize: 3, numBad: 3, seed: 271 }, + { totalProofs: 10, batchSize: 10, numBad: 1, seed: 555 }, + { totalProofs: 4, batchSize: 2, numBad: 4, seed: 0 }, + ]) { + it(`random pattern: ${numBad} bad in ${totalProofs}, batchSize=${batchSize}, seed=${seed}`, async () => { + const rng = seededRandom(seed); + const indices = Array.from({ length: totalProofs }, (_, i) => i); + for (let i = indices.length - 1; i > 0; i--) { + const j = Math.floor(rng() * (i + 1)); + [indices[i], indices[j]] = [indices[j], indices[i]]; + } + const badIndices = new Set(indices.slice(0, numBad)); + + const proofs = Array.from({ length: totalProofs }, (_, i) => ({ + vkIndex: 0, + proofFields: badIndices.has(i) ? invalidProofFields : validProofFields, + })); + + const results = await run({ batchSize, proofs }); + expect(results).toHaveLength(totalProofs); + + for (let i = 0; i < totalProofs; i++) { + expect(results[i].valid).toBe(!badIndices.has(i)); + } + }); + } +}); diff --git a/yarn-project/ivc-integration/src/batch_verifier_test_helpers.ts b/yarn-project/ivc-integration/src/batch_verifier_test_helpers.ts new file mode 100644 index 000000000000..18f7417230bc --- /dev/null +++ b/yarn-project/ivc-integration/src/batch_verifier_test_helpers.ts @@ -0,0 +1,32 @@ +import { BatchChonkVerifier } from '@aztec/bb-prover'; +import type { IVCProofVerificationResult } from '@aztec/stdlib/interfaces/server'; + +/** Corrupt flat proof fields by flipping bytes in an early field element. */ +export function corruptProofFields(fields: Uint8Array[]): Uint8Array[] { + const corrupted = fields.map(f => Uint8Array.from(f)); + corrupted[2] = Uint8Array.from(corrupted[2]); + corrupted[2][0] ^= 0xff; + corrupted[2][1] ^= 0xff; + return corrupted; +} + +/** Create a BatchChonkVerifier, enqueue proofs, collect results, and stop. */ +export async function runBatchVerifier(opts: { + vks: Uint8Array[]; + numCores: number; + batchSize: number; + proofs: { vkIndex: number; proofFields: Uint8Array[] }[]; +}): Promise { + const verifier = await BatchChonkVerifier.newForTesting( + { bbChonkVerifyConcurrency: opts.numCores }, + opts.vks, + opts.batchSize, + ); + + try { + const promises = opts.proofs.map(p => verifier.enqueueProof(p.vkIndex, p.proofFields)); + return await Promise.all(promises); + } finally { + await verifier.stop(); + } +} diff --git a/yarn-project/node-keystore/src/keystore_manager.test.ts b/yarn-project/node-keystore/src/keystore_manager.test.ts index b2861a964a8f..85f355b5fcc2 100644 --- a/yarn-project/node-keystore/src/keystore_manager.test.ts +++ b/yarn-project/node-keystore/src/keystore_manager.test.ts @@ -12,6 +12,7 @@ import { join } from 'path'; import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; import { KeystoreError, KeystoreManager } from '../src/keystore_manager.js'; +import { ethPrivateKeySchema } from '../src/schemas.js'; import { LocalSigner, RemoteSigner } from '../src/signer.js'; import type { KeyStore } from '../src/types.js'; @@ -1560,4 +1561,47 @@ describe('KeystoreManager', () => { expect(validateAccessSpy).toHaveBeenCalledWith(url2, [address2.toString()]); }); }); + + describe('createFundingSigner', () => { + const fundingPrivateKey = ethPrivateKeySchema.parse( + '0x1234567890123456789012345678901234567890123456789012345678901234', + ); + + it('returns signer from top-level fundingAccount', async () => { + const keystore: KeyStore = { + schemaVersion: 1, + validators: [ + { + attester: EthAddress.random(), + feeRecipient: await AztecAddress.random(), + }, + ], + fundingAccount: fundingPrivateKey, + }; + + const manager = new KeystoreManager(keystore); + const signer = manager.createFundingSigner(); + + expect(signer).toBeDefined(); + const expected = new LocalSigner(Buffer32.fromString(fundingPrivateKey)); + expect(signer!.address.equals(expected.address)).toBeTruthy(); + }); + + it('returns undefined when no fundingAccount configured', async () => { + const keystore: KeyStore = { + schemaVersion: 1, + validators: [ + { + attester: EthAddress.random(), + feeRecipient: await AztecAddress.random(), + }, + ], + }; + + const manager = new KeystoreManager(keystore); + const signer = manager.createFundingSigner(); + + expect(signer).toBeUndefined(); + }); + }); }); diff --git a/yarn-project/node-keystore/src/keystore_manager.ts b/yarn-project/node-keystore/src/keystore_manager.ts index 23391fe6bbc1..6bacbf6cc356 100644 --- a/yarn-project/node-keystore/src/keystore_manager.ts +++ b/yarn-project/node-keystore/src/keystore_manager.ts @@ -270,6 +270,15 @@ export class KeystoreManager { return allPublishers; } + /** Create a signer for the top-level funding account, if configured. */ + createFundingSigner(): EthSigner | undefined { + const fundingAccount = this.keystore.fundingAccount; + if (!fundingAccount) { + return undefined; + } + return this.createSignerFromEthAccount(fundingAccount, this.keystore.remoteSigner); + } + /** * Create signers for slasher accounts */ diff --git a/yarn-project/p2p/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts b/yarn-project/p2p/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts index 44ad0f6eccc1..c756de610980 100644 --- a/yarn-project/p2p/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +++ b/yarn-project/p2p/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts @@ -1,5 +1,4 @@ import { MockL2BlockSource } from '@aztec/archiver/test'; -import type { EpochCache } from '@aztec/epoch-cache'; import { SecretValue } from '@aztec/foundation/config'; import { createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; @@ -15,7 +14,6 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien import type { PeerId } from '@libp2p/interface'; import { peerIdFromString } from '@libp2p/peer-id'; -import { mock } from 'jest-mock-extended'; import type { P2PConfig } from '../../../config.js'; import { BatchTxRequesterCollector, SendBatchRequestCollector } from '../../../services/index.js'; @@ -29,6 +27,7 @@ import { InMemoryTxPool, UNLIMITED_RATE_LIMIT_QUOTA, calculateInternalTimeout, + createMockEpochCache, createMockWorldStateSynchronizer, } from '../../../test-helpers/index.js'; import { createP2PClient } from '../../index.js'; @@ -99,7 +98,7 @@ function sendMessage(message: WorkerResponse): Promise { async function startClient(config: P2PConfig, clientIndex: number) { txPool = new InMemoryTxPool(); attestationPool = new InMemoryAttestationPool(); - const epochCache = mock(); + const epochCache = createMockEpochCache(); const worldState = createMockWorldStateSynchronizer(); const l2BlockSource = new MockL2BlockSource(); const proofVerifier = new AlwaysTrueCircuitVerifier(); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/contract_instance_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/contract_instance_validator.test.ts new file mode 100644 index 000000000000..e3f01949139a --- /dev/null +++ b/yarn-project/p2p/src/msg_validators/tx_validator/contract_instance_validator.test.ts @@ -0,0 +1,121 @@ +import { PRIVATE_LOG_SIZE_IN_FIELDS } from '@aztec/constants'; +import { padArrayEnd } from '@aztec/foundation/collection'; +import { Fr } from '@aztec/foundation/curves/bn254'; +import { CONTRACT_INSTANCE_PUBLISHED_EVENT_TAG } from '@aztec/protocol-contracts'; +import { AztecAddress } from '@aztec/stdlib/aztec-address'; +import { computeContractAddressFromInstance } from '@aztec/stdlib/contract'; +import { PublicKeys } from '@aztec/stdlib/keys'; +import { PrivateLog } from '@aztec/stdlib/logs'; +import { mockTxForRollup } from '@aztec/stdlib/testing'; +import { + TX_ERROR_INCORRECT_CONTRACT_ADDRESS, + TX_ERROR_MALFORMED_CONTRACT_INSTANCE_LOG, + type Tx, +} from '@aztec/stdlib/tx'; + +import { ContractInstanceTxValidator } from './contract_instance_validator.js'; + +describe('ContractInstanceTxValidator', () => { + let validator: ContractInstanceTxValidator; + + beforeEach(() => { + validator = new ContractInstanceTxValidator(); + }); + + const expectValid = async (tx: Tx) => { + await expect(validator.validateTx(tx)).resolves.toEqual({ result: 'valid' }); + }; + + const expectInvalid = async (tx: Tx, reason: string) => { + await expect(validator.validateTx(tx)).resolves.toEqual({ result: 'invalid', reason: [reason] }); + }; + + /** + * Builds a PrivateLog encoding a ContractInstancePublishedEvent. + * Layout: [tag, address, version, salt, contractClassId, initializationHash, ...publicKeys(8 fields), deployer] + */ + async function buildContractInstanceLog(opts?: { address?: AztecAddress }): Promise { + const salt = Fr.random(); + const contractClassId = Fr.random(); + const initializationHash = Fr.random(); + const publicKeys = await PublicKeys.random(); + const deployer = await AztecAddress.random(); + + const instance = { + version: 1 as const, + salt, + currentContractClassId: contractClassId, + originalContractClassId: contractClassId, + initializationHash, + publicKeys, + deployer, + }; + + const correctAddress = await computeContractAddressFromInstance(instance); + const address = opts?.address ?? correctAddress; + + // Serialize the event into fields matching the format expected by ContractInstancePublishedEvent.fromLog. + // fromLog reads from a buffer: [tag(32 bytes) | address(32) | version(32) | salt(32) | classId(32) | initHash(32) | publicKeys(4*64=256 bytes) | deployer(32)] + // PublicKeys serializes as 4 Points, each Point is 2 Fr (x, y) = 64 bytes. Total: 8 Fr fields. + const publicKeysBuffer = publicKeys.toBuffer(); + const publicKeysFields: Fr[] = []; + for (let i = 0; i < publicKeysBuffer.length; i += 32) { + publicKeysFields.push(Fr.fromBuffer(publicKeysBuffer.subarray(i, i + 32))); + } + + const emittedFields: Fr[] = [ + CONTRACT_INSTANCE_PUBLISHED_EVENT_TAG, + address.toField(), + new Fr(1), // version + salt, + contractClassId, + initializationHash, + ...publicKeysFields, + deployer.toField(), + ]; + const emittedLength = emittedFields.length; + + const fields = padArrayEnd(emittedFields, Fr.ZERO, PRIVATE_LOG_SIZE_IN_FIELDS); + return new PrivateLog(fields as any, emittedLength); + } + + function injectPrivateLog(tx: Tx, log: PrivateLog) { + // For a rollup-only tx, private logs live in forRollup.end.privateLogs + const privateLogs = tx.data.forRollup!.end.privateLogs; + const emptyIdx = privateLogs.findIndex(l => l.isEmpty()); + if (emptyIdx >= 0) { + privateLogs[emptyIdx] = log; + } else { + throw new Error('No empty private log slot available in mock tx'); + } + } + + it('allows transactions with no contract instance logs', async () => { + const tx = await mockTxForRollup(1); + await expectValid(tx); + }); + + it('allows transactions with correct contract instance addresses', async () => { + const tx = await mockTxForRollup(2); + const log = await buildContractInstanceLog(); + injectPrivateLog(tx, log); + await expectValid(tx); + }); + + it('rejects transactions with incorrect contract instance addresses', async () => { + const tx = await mockTxForRollup(3); + const wrongAddress = await AztecAddress.random(); + const log = await buildContractInstanceLog({ address: wrongAddress }); + injectPrivateLog(tx, log); + await expectInvalid(tx, TX_ERROR_INCORRECT_CONTRACT_ADDRESS); + }); + + it('rejects transactions with malformed contract instance logs', async () => { + const tx = await mockTxForRollup(4); + // Create a log that has the right tag but garbage data + const fields = padArrayEnd([CONTRACT_INSTANCE_PUBLISHED_EVENT_TAG], Fr.ZERO, PRIVATE_LOG_SIZE_IN_FIELDS); + const malformedLog = new PrivateLog(fields as any, 1); + injectPrivateLog(tx, malformedLog); + await expectInvalid(tx, TX_ERROR_MALFORMED_CONTRACT_INSTANCE_LOG); + }); +}); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/contract_instance_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/contract_instance_validator.ts new file mode 100644 index 000000000000..ee8d39825066 --- /dev/null +++ b/yarn-project/p2p/src/msg_validators/tx_validator/contract_instance_validator.ts @@ -0,0 +1,56 @@ +import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log'; +import { ContractInstancePublishedEvent } from '@aztec/protocol-contracts/instance-registry'; +import { computeContractAddressFromInstance } from '@aztec/stdlib/contract'; +import { + TX_ERROR_INCORRECT_CONTRACT_ADDRESS, + TX_ERROR_MALFORMED_CONTRACT_INSTANCE_LOG, + type Tx, + type TxValidationResult, + type TxValidator, +} from '@aztec/stdlib/tx'; + +/** Validates that contract instance deployment logs contain correct addresses. */ +export class ContractInstanceTxValidator implements TxValidator { + #log: Logger; + + constructor(bindings?: LoggerBindings) { + this.#log = createLogger('p2p:tx_validator:contract_instance', bindings); + } + + async validateTx(tx: Tx): Promise { + const reason = await this.#hasCorrectContractInstanceAddresses(tx); + return reason ? { result: 'invalid', reason: [reason] } : { result: 'valid' }; + } + + async #hasCorrectContractInstanceAddresses(tx: Tx): Promise { + const privateLogs = tx.data.getNonEmptyPrivateLogs(); + for (const log of privateLogs) { + if (!ContractInstancePublishedEvent.isContractInstancePublishedEvent(log)) { + continue; + } + + let event; + try { + event = ContractInstancePublishedEvent.fromLog(log); + } catch (e) { + this.#log.warn(`Rejecting tx ${tx.getTxHash()}: failed to parse contract instance event: ${e}`); + return TX_ERROR_MALFORMED_CONTRACT_INSTANCE_LOG; + } + + try { + const instance = event.toContractInstance(); + const computedAddress = await computeContractAddressFromInstance(instance); + if (!computedAddress.equals(instance.address)) { + this.#log.warn( + `Rejecting tx ${tx.getTxHash()}: contract instance address mismatch. Claimed ${instance.address}, computed ${computedAddress}`, + ); + return TX_ERROR_INCORRECT_CONTRACT_ADDRESS; + } + } catch (e) { + this.#log.warn(`Rejecting tx ${tx.getTxHash()}: failed to compute contract instance address: ${e}`); + return TX_ERROR_MALFORMED_CONTRACT_INSTANCE_LOG; + } + } + return undefined; + } +} diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.test.ts index 2820c3a108b6..3f3c4ae17d00 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.test.ts @@ -1,14 +1,18 @@ import { CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, + CONTRACT_CLASS_PUBLISHED_MAGIC_VALUE, MAX_CONTRACT_CLASS_LOGS_PER_TX, MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS, } from '@aztec/constants'; import { timesParallel } from '@aztec/foundation/collection'; import { randomInt } from '@aztec/foundation/crypto/random'; import { Fr } from '@aztec/foundation/curves/bn254'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; +import { bufferAsFields } from '@aztec/stdlib/abi'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; +import { computeContractClassId, computePublicBytecodeCommitment } from '@aztec/stdlib/contract'; import { LogHash, ScopedLogHash } from '@aztec/stdlib/kernel'; -import { ContractClassLogFields } from '@aztec/stdlib/logs'; +import { ContractClassLog, ContractClassLogFields } from '@aztec/stdlib/logs'; import { mockTx } from '@aztec/stdlib/testing'; import { TX_ERROR_CALLDATA_COUNT_MISMATCH, @@ -17,6 +21,8 @@ import { TX_ERROR_CONTRACT_CLASS_LOG_COUNT, TX_ERROR_CONTRACT_CLASS_LOG_LENGTH, TX_ERROR_INCORRECT_CALLDATA, + TX_ERROR_INCORRECT_CONTRACT_CLASS_ID, + TX_ERROR_MALFORMED_CONTRACT_CLASS_LOG, type Tx, } from '@aztec/stdlib/tx'; @@ -243,4 +249,119 @@ describe('TxDataValidator', () => { await expectInvalid(badTxs[0], TX_ERROR_CONTRACT_CLASS_LOG_LENGTH); }); + + describe('contract class id validation', () => { + /** + * Builds a ContractClassLog encoding a ContractClassPublishedEvent. + * Layout: [magic, contractClassId, version, artifactHash, privateFunctionsRoot, ...bytecodeAsFields] + */ + async function buildContractClassLog(opts?: { contractClassId?: Fr }): Promise<{ + log: ContractClassLog; + emittedLength: number; + }> { + const artifactHash = Fr.random(); + const privateFunctionsRoot = Fr.random(); + const packedBytecode = Buffer.from('aabbccdd', 'hex'); + + const bytecodeCommitment = await computePublicBytecodeCommitment(packedBytecode); + const correctClassId = await computeContractClassId({ + artifactHash, + privateFunctionsRoot, + publicBytecodeCommitment: bytecodeCommitment, + }); + const contractClassId = opts?.contractClassId ?? correctClassId; + + const bytecodeFields = bufferAsFields(packedBytecode, CONTRACT_CLASS_LOG_SIZE_IN_FIELDS); + let lastNonZero = bytecodeFields.length - 1; + while (lastNonZero >= 0 && bytecodeFields[lastNonZero].isZero()) { + lastNonZero--; + } + const bytecodeEmittedFields = bytecodeFields.slice(0, lastNonZero + 1); + + const headerFields = [ + new Fr(CONTRACT_CLASS_PUBLISHED_MAGIC_VALUE), + contractClassId, + new Fr(1), // version + artifactHash, + privateFunctionsRoot, + ]; + + const emittedFields = [...headerFields, ...bytecodeEmittedFields]; + const emittedLength = emittedFields.length; + + const allFields = [ + ...emittedFields, + ...Array(CONTRACT_CLASS_LOG_SIZE_IN_FIELDS - emittedFields.length).fill(Fr.ZERO), + ]; + + const fields = new ContractClassLogFields(allFields); + const log = new ContractClassLog(ProtocolContractAddress.ContractClassRegistry, fields, emittedLength); + return { log, emittedLength }; + } + + async function injectContractClassLog(tx: Tx, log: ContractClassLog, emittedLength: number) { + tx.contractClassLogFields.push(log.fields); + const logHashes = tx.data.forPublic!.nonRevertibleAccumulatedData.contractClassLogsHashes; + const emptyIdx = logHashes.findIndex(h => h.isEmpty()); + if (emptyIdx >= 0) { + logHashes[emptyIdx] = LogHash.from({ + value: await log.fields.hash(), + length: emittedLength, + }).scope(log.contractAddress); + } + } + + it('allows transactions with correct contract class ids', async () => { + const tx = await mockTx(2, { + numberOfNonRevertiblePublicCallRequests: 1, + numberOfRevertiblePublicCallRequests: 0, + }); + const { log, emittedLength } = await buildContractClassLog(); + await injectContractClassLog(tx, log, emittedLength); + await tx.recomputeHash(); + await expect(validator.validateTx(tx)).resolves.toEqual({ result: 'valid' }); + }); + + it('rejects transactions with incorrect contract class ids', async () => { + const tx = await mockTx(3, { + numberOfNonRevertiblePublicCallRequests: 1, + numberOfRevertiblePublicCallRequests: 0, + }); + const { log, emittedLength } = await buildContractClassLog({ contractClassId: Fr.random() }); + await injectContractClassLog(tx, log, emittedLength); + await tx.recomputeHash(); + await expect(validator.validateTx(tx)).resolves.toEqual({ + result: 'invalid', + reason: [TX_ERROR_INCORRECT_CONTRACT_CLASS_ID], + }); + }); + + it('rejects transactions with malformed contract class logs', async () => { + const tx = await mockTx(4, { + numberOfNonRevertiblePublicCallRequests: 1, + numberOfRevertiblePublicCallRequests: 0, + }); + const headerFields = [ + new Fr(CONTRACT_CLASS_PUBLISHED_MAGIC_VALUE), + Fr.random(), + new Fr(1), + Fr.random(), + Fr.random(), + new Fr(999999), // bogus bytecode length + ]; + const allFields = [ + ...headerFields, + ...Array(CONTRACT_CLASS_LOG_SIZE_IN_FIELDS - headerFields.length).fill(Fr.ZERO), + ]; + const fields = new ContractClassLogFields(allFields); + const log = new ContractClassLog(ProtocolContractAddress.ContractClassRegistry, fields, headerFields.length); + await injectContractClassLog(tx, log, headerFields.length); + await tx.recomputeHash(); + const result = await validator.validateTx(tx); + expect(result.result).toBe('invalid'); + expect(result.result === 'invalid' && result.reason[0]).toMatch( + new RegExp(`${TX_ERROR_INCORRECT_CONTRACT_CLASS_ID}|${TX_ERROR_MALFORMED_CONTRACT_CLASS_LOG}`), + ); + }); + }); }); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.ts index 692e4705e186..7c284b6d0ce3 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.ts @@ -1,5 +1,7 @@ import { MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS } from '@aztec/constants'; import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log'; +import { ContractClassPublishedEvent } from '@aztec/protocol-contracts/class-registry'; +import { computeContractClassId } from '@aztec/stdlib/contract'; import { computeCalldataHash } from '@aztec/stdlib/hash'; import { TX_ERROR_CALLDATA_COUNT_MISMATCH, @@ -9,7 +11,9 @@ import { TX_ERROR_CONTRACT_CLASS_LOG_LENGTH, TX_ERROR_CONTRACT_CLASS_LOG_SORTING, TX_ERROR_INCORRECT_CALLDATA, + TX_ERROR_INCORRECT_CONTRACT_CLASS_ID, TX_ERROR_INCORRECT_HASH, + TX_ERROR_MALFORMED_CONTRACT_CLASS_LOG, Tx, type TxValidationResult, type TxValidator, @@ -26,7 +30,8 @@ export class DataTxValidator implements TxValidator { const reason = (await this.#hasCorrectHash(tx)) ?? (await this.#hasCorrectCalldata(tx)) ?? - (await this.#hasCorrectContractClassLogs(tx)); + (await this.#hasCorrectContractClassLogs(tx)) ?? + (await this.#hasCorrectContractClassIds(tx)); return reason ? { result: 'invalid', reason: [reason] } : { result: 'valid' }; } @@ -127,4 +132,40 @@ export class DataTxValidator implements TxValidator { return undefined; } + + async #hasCorrectContractClassIds(tx: Tx): Promise { + const contractClassLogs = tx.getContractClassLogs(); + for (const log of contractClassLogs) { + if (!ContractClassPublishedEvent.isContractClassPublishedEvent(log)) { + continue; + } + + let event; + try { + event = ContractClassPublishedEvent.fromLog(log); + } catch (e) { + this.#log.warn(`Rejecting tx ${tx.getTxHash()}: failed to parse contract class event: ${e}`); + return TX_ERROR_MALFORMED_CONTRACT_CLASS_LOG; + } + + try { + const { publicBytecodeCommitment } = await event.toContractClassPublicWithBytecodeCommitment(); + const computedClassId = await computeContractClassId({ + artifactHash: event.artifactHash, + privateFunctionsRoot: event.privateFunctionsRoot, + publicBytecodeCommitment, + }); + if (!computedClassId.equals(event.contractClassId)) { + this.#log.warn( + `Rejecting tx ${tx.getTxHash()}: contract class id mismatch. Claimed ${event.contractClassId}, computed ${computedClassId}`, + ); + return TX_ERROR_INCORRECT_CONTRACT_CLASS_ID; + } + } catch (e) { + this.#log.warn(`Rejecting tx ${tx.getTxHash()}: failed to compute contract class id: ${e}`); + return TX_ERROR_MALFORMED_CONTRACT_CLASS_LOG; + } + } + return undefined; + } } diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/factory.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/factory.test.ts index 92ede7a4afaa..6dc67ca9294a 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/factory.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/factory.test.ts @@ -14,6 +14,7 @@ import { type MockProxy, mock } from 'jest-mock-extended'; import { AggregateTxValidator } from './aggregate_tx_validator.js'; import { BlockHeaderTxValidator } from './block_header_validator.js'; +import { ContractInstanceTxValidator } from './contract_instance_validator.js'; import { DataTxValidator } from './data_validator.js'; import { DoubleSpendTxValidator } from './double_spend_validator.js'; import { @@ -73,6 +74,7 @@ describe('Validator factory functions', () => { 'doubleSpendValidator', 'gasValidator', 'dataValidator', + 'contractInstanceValidator', ]); }); @@ -170,6 +172,7 @@ describe('Validator factory functions', () => { MetadataTxValidator.name, SizeTxValidator.name, DataTxValidator.name, + ContractInstanceTxValidator.name, TxProofValidator.name, ]); }); @@ -187,6 +190,7 @@ describe('Validator factory functions', () => { MetadataTxValidator.name, SizeTxValidator.name, DataTxValidator.name, + ContractInstanceTxValidator.name, TxProofValidator.name, ]); }); @@ -221,6 +225,7 @@ describe('Validator factory functions', () => { BlockHeaderTxValidator.name, DoubleSpendTxValidator.name, DataTxValidator.name, + ContractInstanceTxValidator.name, GasTxValidator.name, TxProofValidator.name, ]); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/factory.ts b/yarn-project/p2p/src/msg_validators/tx_validator/factory.ts index 849f105b46be..4d4df944a34f 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/factory.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/factory.ts @@ -53,6 +53,7 @@ import type { TxMetaData } from '../../mem_pools/tx_pool_v2/tx_metadata.js'; import { AggregateTxValidator } from './aggregate_tx_validator.js'; import { ArchiveCache } from './archive_cache.js'; import { type ArchiveSource, BlockHeaderTxValidator } from './block_header_validator.js'; +import { ContractInstanceTxValidator } from './contract_instance_validator.js'; import { DataTxValidator } from './data_validator.js'; import { DoubleSpendTxValidator, type NullifierSource } from './double_spend_validator.js'; import { GasLimitsValidator, GasTxValidator } from './gas_validator.js'; @@ -167,6 +168,10 @@ export function createFirstStageTxValidationsForGossipedTransactions( validator: new DataTxValidator(bindings), severity: PeerErrorSeverity.MidToleranceError, }, + contractInstanceValidator: { + validator: new ContractInstanceTxValidator(bindings), + severity: PeerErrorSeverity.MidToleranceError, + }, }; } @@ -218,6 +223,7 @@ function createTxValidatorForMinimumTxIntegrityChecks( ), new SizeTxValidator(bindings), new DataTxValidator(bindings), + new ContractInstanceTxValidator(bindings), new TxProofValidator(verifier, bindings), ); } @@ -321,6 +327,7 @@ export function createTxValidatorForAcceptingTxsOverRPC( new BlockHeaderTxValidator(new ArchiveCache(db), bindings), new DoubleSpendTxValidator(new NullifierCache(db), bindings), new DataTxValidator(bindings), + new ContractInstanceTxValidator(bindings), ]; if (!skipFeeEnforcement) { diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.ts index 69d5bd9f0cab..39362bd39dd2 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.ts @@ -40,7 +40,7 @@ export class PhasesTxValidator implements TxValidator { // which are needed for public FPC flows, but fail if the account contract hasnt been deployed yet, // which is what we're trying to do as part of the current txs. // We only need to create/revert checkpoint here because of this addNewContracts call. - await this.contractsDB.addNewContracts(tx); + this.contractsDB.addNewContracts(tx); if (!tx.data.forPublic) { this.#log.debug( diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index f7dae99a092f..7235ecb51bd8 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -130,7 +130,7 @@ type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: Vali // REFACTOR: Unify with the type above type ReceivedMessageValidationResult = | { obj: T; result: Exclude; metadata?: M } - | { obj?: T; result: TopicValidatorResult.Reject; metadata?: M }; + | { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity }; /** * Lib P2P implementation of the P2PService interface. @@ -882,30 +882,56 @@ export class LibP2PService extends WithTracer implements P2PService { source: PeerId, topicType: TopicType, ): Promise> { - let resultAndObj: ReceivedMessageValidationResult = { result: TopicValidatorResult.Reject }; + // Default to reject result with a penalty if validation function throws an error + let resultAndObj: ReceivedMessageValidationResult = { + result: TopicValidatorResult.Reject, + severity: PeerErrorSeverity.MidToleranceError, + }; const timer = new Timer(); try { resultAndObj = await validationFunc(); } catch (err) { - this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError); - this.logger.error(`Error deserializing and validating gossipsub message`, err, { - msgId, - source: source.toString(), - topicType, - }); + this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType }); } if (resultAndObj.result === TopicValidatorResult.Accept) { + this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType }); this.instrumentation.recordMessageValidation(topicType, timer); + } else if (resultAndObj.result === TopicValidatorResult.Reject) { + this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, { + msgId, + source: source.toString(), + topicType, + severity: resultAndObj.severity, + }); + this.peerManager.penalizePeer(source, resultAndObj.severity); + } else { + this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType }); } this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result); return resultAndObj; } + private tryDeserialize(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined { + try { + return deserializeFunc(); + } catch (err) { + this.logger.warn(`Failed to deserialize gossipsub message from buffer`, { + err, + msgId, + source: source.toString(), + }); + return undefined; + } + } + protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) { const validationFunc: () => Promise> = async () => { - const tx = Tx.fromBuffer(payloadData); + const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source); + if (!tx) { + return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError }; + } const currentBlockNumber = await this.archiver.getBlockNumber(); const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot(); @@ -930,8 +956,7 @@ export class LibP2PService extends WithTracer implements P2PService { severity, source: source.toString(), }); - this.peerManager.penalizePeer(source, severity); - return { result: TopicValidatorResult.Reject }; + return { result: TopicValidatorResult.Reject, severity }; } // Pool pre-check: see if the pool would accept this tx before doing expensive proof verification @@ -953,8 +978,7 @@ export class LibP2PService extends WithTracer implements P2PService { severity, source: source.toString(), }); - this.peerManager.penalizePeer(source, severity); - return { result: TopicValidatorResult.Reject }; + return { result: TopicValidatorResult.Reject, severity }; } // Pool add: persist the tx @@ -979,8 +1003,7 @@ export class LibP2PService extends WithTracer implements P2PService { source: source.toString(), txHash: txHash.toString(), }); - this.peerManager.penalizePeer(source, PeerErrorSeverity.HighToleranceError); - return { result: TopicValidatorResult.Reject }; + return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError }; } }; @@ -1010,7 +1033,16 @@ export class LibP2PService extends WithTracer implements P2PService { source: PeerId, ): Promise { const { result, obj: attestation } = await this.validateReceivedMessage( - () => this.validateAndStoreCheckpointAttestation(source, CheckpointAttestation.fromBuffer(payloadData)), + () => { + const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source); + if (!attestation) { + return Promise.resolve({ + result: TopicValidatorResult.Reject, + severity: PeerErrorSeverity.LowToleranceError, + }); + } + return this.validateAndStoreCheckpointAttestation(source, attestation); + }, msgId, source, TopicType.checkpoint_attestation, @@ -1043,8 +1075,7 @@ export class LibP2PService extends WithTracer implements P2PService { if (validationResult.result === 'reject') { this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`); - this.peerManager.penalizePeer(peerId, validationResult.severity); - return { result: TopicValidatorResult.Reject }; + return { result: TopicValidatorResult.Reject, severity: validationResult.severity }; } if (validationResult.result === 'ignore') { @@ -1070,16 +1101,16 @@ export class LibP2PService extends WithTracer implements P2PService { return { result: TopicValidatorResult.Ignore, obj: attestation }; } - // Could not add (cap reached for signer), no need to re-broadcast + // Could not add (cap reached for signer), penalize and do not re-broadcast if (!added) { - this.logger.warn(`Dropping checkpoint attestation due to cap`, { + this.logger.warn(`Rejecting checkpoint attestation due to cap`, { slot: slot.toString(), archive: attestation.archive.toString(), source: peerId.toString(), attester: attestation.getSender()?.toString(), count, }); - return { result: TopicValidatorResult.Ignore, obj: attestation }; + return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError }; } // Check if this is a duplicate attestation (signer attested to a different proposal at the same slot) @@ -1134,8 +1165,7 @@ export class LibP2PService extends WithTracer implements P2PService { if (validationResult.result === 'reject') { this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`); - this.peerManager.penalizePeer(peerId, validationResult.severity); - return { result: TopicValidatorResult.Reject }; + return { result: TopicValidatorResult.Reject, severity: validationResult.severity }; } if (validationResult.result === 'ignore') { @@ -1159,7 +1189,6 @@ export class LibP2PService extends WithTracer implements P2PService { // Too many blocks received for this slot and index, penalize peer and do not re-broadcast if (!added) { - this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError); this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, { ...block.toBlockInfo(), indexWithinCheckpoint: block.indexWithinCheckpoint, @@ -1167,7 +1196,11 @@ export class LibP2PService extends WithTracer implements P2PService { proposer: block.getSender()?.toString(), source: peerId.toString(), }); - return { result: TopicValidatorResult.Reject, metadata: { isEquivocated } }; + return { + result: TopicValidatorResult.Reject, + metadata: { isEquivocated }, + severity: PeerErrorSeverity.HighToleranceError, + }; } // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback, @@ -1260,8 +1293,7 @@ export class LibP2PService extends WithTracer implements P2PService { if (validationResult.result === 'reject') { this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`); - this.peerManager.penalizePeer(peerId, validationResult.severity); - return { result: TopicValidatorResult.Reject }; + return { result: TopicValidatorResult.Reject, severity: validationResult.severity }; } if (validationResult.result === 'ignore') { @@ -1276,20 +1308,21 @@ export class LibP2PService extends WithTracer implements P2PService { [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(), [Attributes.P2P_ID]: peerId.toString(), }); - const { - result, - obj, - metadata: { isEquivocated } = {}, - } = await this.validateAndStoreBlockProposal(peerId, blockProposal); - if (result === TopicValidatorResult.Reject || !obj || isEquivocated) { + const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal); + const { obj, metadata: { isEquivocated } = {} } = blockProposalResult; + if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) { this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, { [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(), [Attributes.P2P_ID]: peerId.toString(), isEquivocated, - result, + result: blockProposalResult.result, }); - return { result: TopicValidatorResult.Reject }; - } else if (result === TopicValidatorResult.Accept && obj && !isEquivocated) { + return { + result: TopicValidatorResult.Reject, + severity: + 'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError, + }; + } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) { processBlock = true; } } @@ -1316,13 +1349,17 @@ export class LibP2PService extends WithTracer implements P2PService { // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast // Note: We still return the checkpoint obj so the lastBlock can be processed if valid if (!added) { - this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError); this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, { ...checkpoint.toCheckpointInfo(), count, source: peerId.toString(), }); - return { result: TopicValidatorResult.Reject, obj: checkpoint, metadata: { isEquivocated, processBlock } }; + return { + result: TopicValidatorResult.Reject, + obj: checkpoint, + metadata: { isEquivocated, processBlock }, + severity: PeerErrorSeverity.HighToleranceError, + }; } // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback, diff --git a/yarn-project/p2p/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts b/yarn-project/p2p/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts index 98e34b1e9c2e..08d2c613a569 100644 --- a/yarn-project/p2p/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +++ b/yarn-project/p2p/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts @@ -514,6 +514,9 @@ export class BatchTxRequester { }); if (hasInvalidTx) { + this.logger.warn(`Penalizing peer ${peerId.toString()} for sending invalid transactions in batch response`, { + peerId, + }); this.peers.penalisePeer(peerId, PeerErrorSeverity.LowToleranceError); } else { // If we have received successful response from the peer, they have "redeemed" themselves and not considered bad anymore diff --git a/yarn-project/p2p/src/testbench/p2p_client_testbench_worker.ts b/yarn-project/p2p/src/testbench/p2p_client_testbench_worker.ts index 14d733793413..1d3224d94a33 100644 --- a/yarn-project/p2p/src/testbench/p2p_client_testbench_worker.ts +++ b/yarn-project/p2p/src/testbench/p2p_client_testbench_worker.ts @@ -4,7 +4,7 @@ * Used when running testbench commands. */ import { MockL2BlockSource } from '@aztec/archiver/test'; -import type { EpochCache, EpochCacheInterface } from '@aztec/epoch-cache'; +import type { EpochCacheInterface } from '@aztec/epoch-cache'; import { BlockNumber } from '@aztec/foundation/branded-types'; import { SecretValue } from '@aztec/foundation/config'; import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'; @@ -28,7 +28,6 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien import type { Message, PeerId } from '@libp2p/interface'; import { TopicValidatorResult } from '@libp2p/interface'; import { peerIdFromString } from '@libp2p/peer-id'; -import { mock } from 'jest-mock-extended'; import type { P2PClient } from '../client/index.js'; import type { P2PConfig } from '../config.js'; @@ -50,6 +49,7 @@ import { InMemoryAttestationPool, InMemoryTxPool, UNLIMITED_RATE_LIMIT_QUOTA, + createMockEpochCache, createMockWorldStateSynchronizer, filterTxsByDistribution, } from '../test-helpers/index.js'; @@ -344,7 +344,7 @@ process.on('message', async msg => { workerConfig = config; workerTxPool = new InMemoryTxPool(); workerAttestationPool = new InMemoryAttestationPool(); - const epochCache = mock(); + const epochCache = createMockEpochCache(); const worldState = createMockWorldStateSynchronizer(); const l2BlockSource = new MockL2BlockSource(); diff --git a/yarn-project/protocol-contracts/fixtures/PrivateFunctionBroadcastedEventData.hex b/yarn-project/protocol-contracts/fixtures/PrivateFunctionBroadcastedEventData.hex deleted file mode 100644 index a5e89b5c9325..000000000000 --- a/yarn-project/protocol-contracts/fixtures/PrivateFunctionBroadcastedEventData.hex +++ /dev/null @@ -1 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000030ea9d22f6c3686a6866f103431c5796d2be7b7a3d0c3abcf7636b064ab09e6180806ce19f39561cf9bc58615c0541921fc90cae11394eb97f9371f02ec497807059234bbc68b8b55e9c52abb3747b811297e35ab73813a96feb74640524fde220d3856d35260777dde79a3415d94a80d95192d933225ca4d3cbcb12613de9f462fd363c29efb017bd7ae341748dd643c73bc19a714b4a288ce5930b64547bf4f1901f8aabbbfc15c27140e1dd356997345e5a992bf9c641e767b55b95c2d1c1501a83cb29c25febaf05115f7802daf31444e3150a1208117178b5b0c4abb5dce2373ea368857ec7af97e7b470d705848e2bf93ed7bef142a490f2119bcf82d8e120157cfaaa49ce3da30f8b47879114977c24b266d58b0ac18b325d878aafddf01c28fe1059ae0237b72334700697bdf465e03df03986fe05200cadeda66bd762d78ed82f93b61ba718b17c2dfe5b52375b4d37cbbed6f1fc98b47614b0cf21b00000000000000000000000000000000000000000000000000000000000000001405f27848f58d8b7bc2744c01f391c38558140988a50bec1e3ad27729fceb12194f6d330fd4fea842e8bf23f950a18b7553b4104fdc5a0e298a59afa59fe8e907e5876f925d7c7b2dfd62320e14f44595d6989526de62bb4719538ce56191aa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c97a6b21eef7f2eaafa09e24b0475a918bd7388c4dec305145849f5f84d1b822202b944028ad70b98d31f63fa89c5151b51d8ebefedd049e6ade196431690630d5fbd6c0000000000000000000000000000000000000000000000000000000000002321001f8b08000000000000ffed9d77601445ffff339fcf91d07b9516a44b51b0770029a14a9162d7189203a221899704c11e7b3704b0778a58282a5810aca8a8fb00164545c12862ef1d7bf96d48b9bd5c2e994bee8d3c7e7fcf3fcfb03bfbfacc00cececccece5c5e6ae19ceb3777494e4e3933d79f9a9c19484ecfccf5073253003272929353b33273720379a9b95901f8fa14f94cfe8ac11929a9a70dce9a35002c2f3375484a4646fea20983c60e4f2accbffb98f4dc4c7f4e8e245a64521b0052331b528b232c32b576ceb7c8d5c62a57079b5275b4c9d4c92653679b4c89005625ef62956b77ab5c5dad72754bccbf6f70203d23237d5af1f97971050573000b0ad625c655fd3f937fefa09c1c7f20f7787f206b6ec19cc275897ba58d0d006c1b7047ef47c7273d9c9f7fec49bdf6fe6cc4ecc7b2e70cd9b67dee77ee2500d0a55563dfecf7e16935c12e8b88d5b2442515b1727c568e3f3d2d2b73e078007f60465e6e4a6e7a5666e1bcf28a718b5b9eee5e9eeae139bf6c1e7439740500f401e883a1252f2cacbe0a5b58dddd43d580cc51368fd9e51c117d09bb59950070a5456bb129e1ca0a4dd1ccc95f3c313d735a86bfa4255417c4a624713b980033b233fcd055760ddda6e8ab4c68d1eb918bfe70f47d74ce5cab62b86cbb02003f527dd3a859fc47aabfb39a9057b9e4b956ed799555ae47ac723d5a83a7640051c2d27bb1b86bbb6769752f8f519e8bfbc457d98df3ab2de287f44289b217009aea0314c5350df6c2c783c935c1e4da60f28960f2c960f2a960f2e960f2990060f2d960725d30f95c30f97c30f94230b93e987c31987c2976a3dcd376d9d600d9657bc12edb4b359a6abc5cf52d9f33e5a3336b827522627d65891a4d355e00f6a41ff7a49ff0a41d77ba01e82bd00dd057434b6f3718acb1ca05ab9a78cd00a233d6a4265ef3a45ff1a43778d2afba35b111fa3af40de89bb5aef0b59ef400c60af3bb4dd0b7a06f4337d7a4b96ca9ba92fa1fd378544db0ef44c436aa5500dd6ff1a49ff4a49ff2a4df712ba508fa2ef43de8d69ab4c2a7ad721559d5c400fb51be922c8bf88c55ae77ad8ab88d53c467ad72bd6755c40f487df97d4f7a009b27fd8127bdd56d4f1f423f827e0cfda42635b1ce2ad7875635f129e7613d006795eb23ab227ec629e2f356b93eb62ae2e7a4f6f4a927fd9927fdb927fd8900db9ebe807e09fd0afa754d6ae205ab5c5f58d5c4379c87b5de2ad7975645fc009653c417ad727d6555c4ef48ede91b4ffa5b4ffa3b4ffa6bb73d7d0ffd01fa0023f4a79ad4c44b56b9beb7aa89eda49ad8ee49ffe049ffe849ffe4d6c4cfd0005fa0bf427fabc9bce5f7aa4b7fea0da350a3d2ffee49ffec496faa3099fb0300fa27f42fe8dfa1df891af56acd1fd53f87c2e027d83fe5495f5c0d1657ed9a00d91f568fe09ff05c2756c865dc5246fba5ed2b0cd66025f7573142b4d5fd4f00348b633e13b32f5e9fa9e451d85572c57015e3bb6c1b964faaadcd38ab3b1100773720fa5bf1c559e5b2bb150dbf958a1759dd8a56bab1b16c4c5e466efac400d4948c94809b9c37377fc990e2bdb194cc5c8bc6109e5736b438392f7ee14900a9fd7a364afabe6df379171ebeeeea0b0eefd9370a6e9c7721e31fcf2dc4450053b8b9f0f9e0ab035f7c25b7bd2269c6147f5a9a3f6d485e60a67f505a9a7b00e3c1383e4fba8e271defc9e4ae3c05d3cf7ad771a22c64027c75e1ab17becd00505dd3303683802f21da898ad54bc957bf6a6ceb5f97f7acc94bc9573fb8dd00eaa9d3e5a1af245f03f81ac2d708bec6d1efceb8cb435677d820dad13cfadd008b35510dd04d58bb176b5cb655817d4d29bb176efca605ecdaaefee56c3c7500dd8c53d76eb36a66d546dd6c36b99a139e875bc6e6736ca3579369a73eb31600b467d6c2ae3e5a909e86cd9c23ce7a29bbfa91af257de4b309e179b2ad4823009fafa5cbb62b706bc6c8571cbf754df66dab7f864d5db2dd48d33466f71fd20026ea14e62f1a1408a4cc2eb46b13ddabaf063b500f8bfa8cf23dbdbc7aa4e500cf7f42aa287e7ecc375aabfc9ef4fecfe9faccfb846a20fc7e233aa446b3c400e06b134cb68ddde76f9bc41acc4a7d6dacc6e676312a63bb0abf1a4a887a08006f6b3384970c0135fd6d5c7557581522f43eebc6fec70d35b93bbb26d1ceaa0049ec16a326b15b6d7f48665112ef5bbd7dec7a5cfb9a7cf63c065d6df7b1db00813f21ea1055d575644d883ab86cbb0277a24c88dcf89da2fe14ac1f5c46b500fb2158d993afbe3c55b7e81dcb93b6afdc1d993def9ecec16462ec7a4267bb006c898935f8fd5c71b5cdb5abb6aabb5561c97a7967abce9768f108a26f689d008ac35bc5b72b65174a776cef82ed86a8dd6bf015516dfce25f193e6a17bf2b00e52bc6fefebb5156c6ec5f11dd6b316815d835c4ddacba5e57de88d52398ec0019bb11ab875db69e1526d5f362596756c3550fab883d29c395db117ad86d3d00b5b7ca65772fbd6ad0a9ac3a750fbbd7487bab5c76f7d23bca01aad0eab9ec00ee361eab8c5ddd21ca6e24d98352d0dddcb25a65ece68e657605ed13edce9200ddeecc2abb665e93e0b15844edc9086cb395d78b11582c02f76604568bc07b00301a581fabdf222c8f36b4cde7445f4645fa2c02f76304ae6311b83f2370bc0045e03d1981132c02efc5085cd722f00046e07a1681073202d7b708bc37237000038bc0fb300237b408bc2f2370238bc0fb310237b608bc3f2370138bc007300002db6cb51dc8086cf36b828318819b5b043e9811b88545e04318815b5a043e009411b89545e0c318815b5b043e9c11b88d45e0231881db5a041ec408dcce2200f06046609b7594218cc0364b004319813b58044e6204ee681178182370278b00c0c319813b5b041ec1089c681178242370178bc0a3188177b7083c9a11b8ab0045e023191fdd6318d0b18c958971562b130f319e4e378be28d67dcf35131fa008543f892a805d55de0b6cad8db5d94b569151328c5ec1e4531fbd8147322a300474c62402733a04733a0c730a0c732a0c731a0c733a02730a02732a02731a0002733a0c90ce8290c680a033a85014d6540d318503f033a95019dc6804e674000d319d05319d0d318d00c067406039ac9806631a0d90ce8e90c688001cd6140007319d03c067426037a06033a8b019dcd809ec9809ec5809ecd809ec3809ecb00809ec7803ae753a8f914ea0514ea8514ea4514eac514ea2514eaa514ea651400eae514ea1514ea9514ea5514ead514ea3514eab5146a01853a87422da450e70052a8f328d4f914ea7514eaf514ea0d14ea8d14ea4d14eacd14ea2d14eaad1400ea6d14eaed14ea1d14ea9d14ea5d14ea020a752185ba88425d4ca1de4da12e00a150efa150efa550efa350efa7509752a8cb2854cadf65382b28d40728d4070029d48728d49514ea2a0af5610af5110af5510af5310a753585fa3885ba8642005d4ba13e41a13e49a13e45a13e4da13e43a13e4ba1aea3509fa3509fa7505f00a050d753a82f52a82f51a82f53a80e850a0af5150a750385fa2a85fa1a85ba0091427d9d427d83427d9342dd44a1be45a1be4da16ea650b750a8ef50a8451400eabb14ea7b14ea560af57d0a751b85fa0185fa2185fa1185fa3185fa0985fa002985fa1985fa3985fa0585fa2585fa1585fa3585fa0d85fa2d85fa1d85fa3d0085fa0385fa2385fa1385ba9d42fd9942fd8542fd9542fd8d42fd9d42fd834200fd9342fd8b42fd9b42fd87418589e3600d072b1cac72b03e0eb60e071b1f2d003666ff253293f0ef85aecba9cc7a5597af6bd1a15da2bf23b50a5d9f51997d00ac4237e05466c39adc51f5d84685365685959ce08d6d82fbda702ab40907db0094836dc6c136e7605b70b02d39d8561c6c6b0e96d41dda72b0ed38d8dd38d800f61c6c070eb62307db8983edccc12672b05d38d8dd39d8ae1c6c370eb63b0700db8383edc9c1f6e2607b73b07b70b07d38d8be1c6c3f0eb63f07bb2707bb1700073b80831dc8c1eecdc1eec3c1eecbc1eec7c1eecfc11ec0c11ec8c11ec4c1001eccc11ec2c11ecac11ec6c11ecec11ec1c10ee2600773b04338d8a11c6c1200073b8c831d1ee57fa9d2123b8253da911cec280e7634077b24073b86831dcb00c18ee360c773b04771b01338d8891cec240e7632077b34077b0c077b2c077b001c077b3c077b02077b22077b12077b32079bccc19ec2c1a670b05338d8540e00368d83f573b05339d8691cec740e369d833d95833d8d83cde0606770b0991c006c16079bcdc19ecec10638d81c0e369783cde3606772b06770b0b338d8d91c00ec991cec591cecd91cec391cecb91cec791cecf91c6c3e077b01077b21077b0011077b31077b09077b29077b19077b39077b05077b25077b15077b35077b0d00077b2d075bc0c1cee1600b39d8b91cec3c0e763e077b1d077b3d077b03077b0023077b13077b33077b0b077b2b077b1b077b3b077b07077b27077b1707bb8000835dc8c12ee2601773b07773b04b38d87b38d87b39d8fb38d8fb39d8a51cec00320e763907bb82837d80837d90837d88835dc9c1aee2601fe6601fe1601fe500601fe3605773b08f73b06b38d8b51cec131cec931cec531cecd31cec331cec00b31cec3a0ef6390ef6790ef6050e763d07fb2207fb1207fb3207eb70b0e060005fe1603770b0af72b0af71b01b39d8d739d83738d83739d84d1cec5b1cecdb001cec660e760b07fb0e075bc4c1becbc1bec7c16ee560dfe760b771b01f70b0001f72b01f71b01f73b09f70b09f72b09f71b09f73b05f70b05f72b05f71b05f0073b0df70b0df72b0df71b0df73b03f70b03f72b03f71b0db39d89f39d85f3800d85f39d8df38d8df39d83f38d83f39d8bf38d8bf39588edb57386e5fe1b87d0085e3f6158edb57386e5fe1b87d259e834de060394e5ea9c7c1d6e760392e5d0069c8c1368a161b3383b334e6dc51939adc51f5d8a68536de5f8e3a5438265d0069cec1724cbac231e90ac7a42b1c93ae704cbac231e90aa93b704cbac231e9000ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c0093ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cba00c231e90ac7a42b1c93ae0ce46039265de198748563d2158e495738265de19800748563d2158e495738265de198748563d295c328ea50e198748563d2158e49005738265de198748563d2158e495738265d19cec1724cbac231e90ac7a42b1c0093ae704cbac231e90ac7a42b1c93ae704cbac231e9ca040e9663d2158e49570038265de198748563d2158e495738265de198748563d2158e495738265de19800748563d2158e495738265de198748563d2158e495738265de198748563d215008e495738265de198748563d2158e495738265de198748563d2158e49573826005d0970b01c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c0093ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cba00c231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac700a42b1c93ae704cbac231e94a2107cb31e90ac7a42b1c93ae704cbac231e90a00c7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c9300ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac20031e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae3cc8c1724cbac231e9000ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c0093ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cba00c231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac700a42b1b39588e495738265de198748563d2158e495738265de198748563d215008e495738265de198748563d2158e495738265de198748563d2158e49573826005de198748563d2158e495738265de198748563d2158e495738265de19874850063d2158e495738265de19874e5070e9663d2158e495738265de198748563d200158e495738265de198748563d2158e495738265de198748563d2558e49573900265de598749563d2558e495739265de598749563d2558e495739265de59874009563d2558e49571b71b01c4bae36e1609b72b01cffad36e76039fe5be5f86f0095e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558e00ff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5b00e5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e300bfd5811c2cc77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2a00c77fab1cffad72fcb77a1807cbf1df2ac77fab1cffad72fcb7caf1df2ac77f00ab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad7200fcb7caf1df2ac77fab1cffad4ee06039fe5be5f86f95e3bf558eff5639fe5b00e5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e300bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff560039fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bfd50007cbf1df2ac77f00ab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad7200fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df002ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1c00ffad1672b01cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab001cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fc00b7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad5af96fc7f867640005668fcc4ccf9ddbb8482fe9d9abf71e7dfaf6ebbfe75e0306eebdcfbefbed007fc081071d7cc8a1871d7ec4a0c14386260d1b3e62e4a8d1478e193b6efc510013264e9a7cf431c71e77fc09279e7472f229295352d3fc53a74d4f3ff5b48c00199959d9a7077272f3669e316bf699679d7dceb9e739e73bf9ce05ce85ce4500cec5ce25cea5ce65cee5ce15ce95ce55ced5ce35ceb54e8133c72974e63af3009cf9ce75cef5ce0dce8dce4dcecdce2dceadce6dceedce1dce9dce5dce026700a1b3c859ecdced2c71ee71ee75ee73ee77963acb9ce5ce0ae701e741e7216700a5b3ca79d879c479d479cc59ed3ceeac71d63a4f384f3a4f394f3bcf38cf3a00eb9ce79ce79d179cf5ce8bce4bcecb8ee3c079c5d9e0bceabce66c745e77de0070de7436396f396f3b9b9d2dce3b4e91f3aef39eb3d579dfd9e67ce07ce87c00e47cec7ce27cea7ce67cee7ce17ce97ce57ced7ce37ceb7ce77ceffce0fce800fce46c777e767e717e757e737e77fe70fe74fe72fe76fe818983313002a330003e983a30f13009307561eac1d4876900d310a6114c639826304d619ac13487006901d312a6154c6b9836306d61dac1ec06d31ea6034c47984e309d611261ba00c0ec0ed315a61b4c77981e303d617ac1f486d903a60f4c5f987e30fd61f68400d90b6600cc4098bd61f681d917663f98fd610e803910e6209883610e81391400e63098c3618e80190433186608cc509824986130c36146c08c841905331ae600489831306361c6c18c87390a6602cc4498493093618e863906e65898e3608e00873901e6449893604e86498639052605660a4c2a4c1a8c1f662acc3498e93000e930a7c29c06930133032613260b261be67498004c0e4c2e4c1ecc4c9833600066c1cc863913e62c98b361ce813917e63c98f361f2612e80b910e622988b61002e81b914e63298cb61ae80b912e62a98ab61ae81b916a600660e4c21cc5c98007930f361ae83b91ee606981b616e82b919e616985b616e83b91de60e983b6100ee825900b3106611cc6298bb6196c0dc03732fcc7d30f7c32c855906b31c660005cc03300fc23c04b3126615ccc3308fc03c0af318cc6a98c761d6c0ac85790002e64998a7609e867906e659987530cfc13c0ff302cc7a9817615e827919c6008101cc2b301b605e85790d6623cceb306fc0bc09b309e62d98b76136c36c81007907a608e65d98f760b6c2bc0fb30de603980f613e82f918e613984f613e8300f91ce60b982f61be82f91ae61b986f61be83f91ee607981f617e82d90ef33300cc2f30bfc2fc06f33bcc1f307fc2fc05f337cc3f903888810844213e481d48003c24015217520f521fd200d210d208d218d204d214d20cd21cd202d212d20a00d21ad206d216d20eb21ba43da403a423a413a4332411d205b23ba42ba41ba4003ba407a427a417a437640f481f485f483f487fc89e90bd20032003217b43f60081ec0bd90fb23fe400c88190832007430e811c0a390c7238e408c820c860c80010c8504812641864386404642464146434e448c818c858c838c878c8519009009089904990c990a321c7408e851c07391e7202e444c849909321c9905320290090299054481ac40f990a9906990e49879c0a390d92019901c9846441b221a7004302901c482e240f3213720664166436e44cc85990b321e740ce859c07391f00920fb9007221e422c8c5904b2097422e835c0eb9027225e42ac8d5906b20d700420a2073208590b9907990f990eb20d7436e80dc08b9097233e416c8ad90db0020b743ee80dc09b90bb200b210b208b21872376409e41ec8bd90fb20f7439600429641964356401e803c087908b212b20af230e411c8a390c720ab218f43d60040d6429e803c09790af234e419c8b3907590e720cf435e80ac87bc087909f20032c48100f20a6403e455c86b908d90d7216f40de846c82bc05791bb219b20500f20ea408f22ee43dc856c8fb906d900f201f423e827c0cf904f229e433c8e700902f205f42be827c0df906f22de43bc8f7901f203f427e826c87fc0cf905f2002be437c8ef903f207f42fe82fc0df9071a07755fca0255a80f5a071a0f4d8000d685d683d68736803684368236863681368536833687b680b684b682b686b60081b685b683ee066d0fed00ed08ed04ed0c4d847681ee0eed0aed06ed0eed0100ed09ed05ed0ddd03da07da17da0fda1fba27742fe800e840e8ded07da0fb4200f783ee0f3d007a20f420e8c1d043a087420f831e0e3d023a083a183a043a14009a041d061d0e1d011d091d051d0d3d123a063a163a0e3a1e7a14740274227400127432f468e831d063a1c7418f879e003d117a12f4646832f414680a740a3400159a06f543a742a7b95bfcee76bcbb75ee6e73bb5bd2eef6b1bbd5eb6ecbba005ba8ee76a7bb35e96e23ba5b7eeef69cbb95e66e7bb95b54ee7692bbf5e36e00d3b85b2aeef687bb55e16e2bb85b00ee72bdbbb4ee2e83bb4bd6eef2b2bb1400ec2edbba4bacee72a8bb74e92e33ba4b82eef29dbbd4e62e8bb94b58ee729300bb34e42ee3b84b2eeef288bb94e12e3bb84b04eee7bcfbe9ed7e26bb9fb4ee00e7a7fba9e87ed6b99f60eee792fb69e37e86b89f0ceef4de9d8abbd366778a00eb4e47dda9a33bcd73a764f74ef0e7e6053287a6e4a614c5f58c33a2be3af1000975ebd56fd0b051e3264d9b356fd1b255eb366ddbedd6be43c74e9d13bbec00deb55bf71e05053715e62f1c949a1e6859b8e1d5842f7f7af9f9690505a587005a871f1a5cb86169838d83d7de5ef7e4b24343c30f8d2adc70c5e483faf69900b6fedbb243c7146e58df282727132bb7951d3a311c7f72e186e95b3e4f76de009b7977d9a194f05cb30b37f438e028d33e77d3d6a2b893f2ef4d9a951df0e700e4a46765ce2da87e75707cb4174c8ff68294682fc889f6027fb41724467b4100eaae574b99d15e306dd7abd6347a9172e9115277bd22a5d11f5c16bdb5465d00a4a9d15e9047bf077ed348ff0f3cb833e8379d4b1fbda3bee9ecffffdadd25005ebbfce1feb4682fe84fafd691f4b13571d7eba2e9f4fe90f81f2852d4fd6100167de4fb3f3923eb4b7fd279f4c6c77f3f748df68213e8d39f2c7a7ff82fcc0004fe4fbed8a35e9b89bbb97073af941ddb91c9a95933b25372d3a764f893b3000229a9eeffcdf4078a41c9670452b2b3fd81a2b8d6f98b866465e6e4cecd5f003c343de04fcd95fcbb4766e6faa7f9030b26ef3db0fa9dcc8ad79ba8ae3f7f0068c5ebe3a28b3f347fe190948c8c390dca394b26f833dc9b9ee98fae24ee76006b1841a225dc5f5c963477f9704856f6ecf25b1aea2d93075e52f246b52ef900d018947ce1c4dcacec3985114a5ae1190d59342cdd9f51fd5fce35585cb2a0005a7aa74df3ef1b9615f0a74fcb2cfee7fccdddf272d333d27367976c9e0f29006face376b4d5a34b9a6a41c19cfca525fbe083d2d28a3b427929e6e42f9e98003e233bc35f529cb260150aeb8baa2a660dcdbf6f707a664af1ae7beeb8ecf90065145d72a41b7ad2f494cc624ab0b19607593c2a6f46f6c8a985e517b4ca5f003a3233ada4a4117bc87e55e88cb63cb77dd3ca510367e42f9ce4f6d63985c100ebcbba6ae91d176ede2b3d2739273dcd9fec9f3ad53de176fc3c37422039e000773b7cc80050def1dbfdcb1d7f582d3bfeb0d246d5b8d68ddf841334e61d5f00bc70b7e49e1b1f1a4c78a3e62f18933533a44396672bb9f326a5394a0f277900b3d6b64e926a5d27267c48f1d641e8c8d0a2e2c8901d489f9992eb1f9933d1006dd5493b1af59092363da1bc49878d0bc140e523437999ef9a3c20727e139e00bff227108c109bc16658ac069bb63b6fb079629abf7880c9cc750794dce4740037584a66aadf4db80f273325a3286ebf7f7964995ccb91657269a3ecf03f3800b254376674ac62cc083933cc1b20e4ccf0f078a5674604cff842cf8c0c9ea900137a6654f04c7ce899d1c13309a1678e0c9ea91b7a664cf04cbdd03363836700ea879e19173cd320f4ccf8e09986a1678e0a9e69147a6642f04ce3d033138300672a8cdc9382679a86b79866b51eb99b4747681a3e7237f3c04247eebd424700eed5a553bae1fee27168c71031b274842872c11107ec886734e2195fc4337500229e898f782621e299ba11cfd48b78a67ec4330d229e6918f14ca388671a47003cd324e299c84fa159f1230a9952c7f65fde97f1ae96df6672507a7515b382008ae7aa1a9b357cb4f41c081b2f3d07c2464ccf81b031d373206cd4f41c081b00373d07c2464ecf81b0b1d373206cf4f41c081b3f3d07c24650cf81b031d47300c0d3c42b9c6bea69e4b199b94d8ed5cc6ddf9d37735b96e62f5e11cacaf127004f77a76b45719dfee599daf05aced486ff0f7f03d6e6eb2ef24cadb67751c900ec21ba8e1127e1b307ef48173a7b685bf6295c59de60635a3879c0c003c2b2007aabb374685e5ad22777fc635cf63c4f860513f3a64418b3c397dfca96b75a000c887babf3d67d67f769bd5fd6b899176d9db4f4dc960b7a7fdaa4ed37798700ccfcad282b723cdf8231791911eeaa66ef0f5f6cc6ade1b11ab73aeebc716b0049466ed988d5e5bf3b6269b4fdacb63dd562c4aa6a8ee219b1c25a6df9a05500e958362c962b55c376ee4a55fb2afafce2a4d3f35232722274edf21e14bca00075fec2e283eef257646879c7b41b31429e5809bd5d551b0ceed4d7a28222070031e541220f6bba8b0d5d893b7165be674eaefb79ecae8e25fb67f953ddaf630077f32d3525757af8827cffffc8d0d6b6d61d5277fad046d8818b7ada14d6c700ca1f6318bb4eb4f55969c7310bdc6f5d6f9f31214b2815ba48cd47d7d0e0c10010e5e1c3efb9c292a3469cfc56f2a15c7aa64ec840b8a365563946457c8995006d3475ab752b19cedea72d1b672b7ddc09151fb706ab2aa4e6ea0633841caf0017acf44a03d42f7dff796394b312c286e1fa5df3171e999592567e203e78d100a289c5a36678e4f8ca23d7ad786b75830da9d20bea55bca05ef08292f7598700d0d77eaf48ad58c35bb1f104f7ae8dae29ddd4da716f233393cade07438a5f0007c57f9973cf087f4af6a0402065b6771c88bc362a85f98b4ab25758a212c600c678cc5ebffd76e25e55aa5bf1c9a5f59e3c352f3375c70b38bd7caf6acf7f00f9a53bba962fddd1a5edb355f8d0121f1da94e382121e62fdd90f5c9d05df000a46022640f3634d7f060a28a5c238209ef92704d3e6022bd9587852fd99697002c6cc1b6bc34250fab75c4cd31137173acf64b3b2377eed24eeff0b5e9c813009a04fa842621f284263e46139a84f057417c8457c1ead221a9f81fe34b92c3004a07a73995bf0612644ea4c17e7ee4ad8b9db5e136bf9a0d969287629d25a400d7d676d665a27875c7877cd896cc4e9654ba7c5bc5baa5543a34553ae92cad008b2a5636436fb6d69521912ac317656554bd32b4c466453346cb02a363352f00e9bff3e6250b33dcda2c8aebf72fcf3e926a39fb482afff9d67f6fffa565a4009e2255cef8fffd0d98b888dfce12f1db59abfd76aefd8fa19262f088a358a800ed19715267ac670fe54f35e2dc4178ef22893077585b3a77281e908705b26600847e4746983e685ca4e9435c6c7ef9f0bfb5f4d063575e7ae81c9bf76252ac00de8b7d77e27279f7ccacdcf4a9b39353037eb789a72567e66564a44f4df70700ca96cbb30359b366fffb7fb612ab3767c3ffe09bb3f67fd092b4b3164a2bfd00bb94b20176ec8ea638a4a4258e2d6b8895fc3d4ac4017317ebc83bf12f525600948471ebc45d6c2bfec1fe8d152ba1552d3b6ccbd834f2b86079cac115871600cbbf768a0bdbc52dabf8b28de18a3125fc19d50d1b982ca39b48d1e3160c4d009f19f6a4821da4fcb6cb2aa270b5f7e1eda8e2e4d3f3dccee0cfccbda162f100ead7f4570ba5d73788f163ac1f0447a80fb9b734a0a75ae282f513e12ab3e300073dc1e7566df6e2df1b85d34366359e7650e1613428bf9dff077cec6cb0cc00c6010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013b \ No newline at end of file diff --git a/yarn-project/protocol-contracts/fixtures/UtilityFunctionBroadcastedEventData.hex b/yarn-project/protocol-contracts/fixtures/UtilityFunctionBroadcastedEventData.hex deleted file mode 100644 index f46d08d02a5f..000000000000 --- a/yarn-project/protocol-contracts/fixtures/UtilityFunctionBroadcastedEventData.hex +++ /dev/null @@ -1 +0,0 @@ -000000000000000000000000000000000000000000000000000000000000000306549ee6586be1c25c81f2d579c06a4c3f31255def74dd729903811d90ba23690806ce19f39561cf9bc58615c0541921fc90cae11394eb97f9371f02ec497807059234bbc68b8b55e9c52abb3747b811297e35ab73813a96feb74640524fde220193cc7bba584f0fb2210f2e762185511bd95a9ade5e06e0e45c3e057128dfd02d1eb762c990e1d198f3bd6ea7ded6f6bba8d8a042dee7ae71b76dc816575049005e3c4cbf3aaaf95376a77f30baf1dcc21581dda5d1eeb8c464aebf2fdbca0d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111cfcfc1125672f40d49b4b192a803a41ccb3ddacec4da05bf7d535224059fa9bf766cd000000000000000000000000000000000000000000000000000000000000118d001f8b08000000000000ffed5d6b6c1c5715f67a76fd58db49e8338ded7d787700671fdef53b4e803f4df374e2244d9af01042e9b659826163bbeb751b8350bb00fca84054c8765b50257e358969155a7ed04aad84848a5a84288b40bcd4d23f0048fd57098100a91245b0aebd3367ee9d73eedcd9879df6fad778cf9c73bf7b00ee79ddc7cc68ab2bdf7f2b72e142f66bc5dc8317660b1766668bb9c26c36bf0070e1c2c2e2e5cbb98b171ecee617730b9e95d28d7b0a33f9fccca583d97cfe00a99695d2da7d33b397f2b9279757565f0fb5d07f9e16e12d1b122fcfe7739e00279797c512575a96573c95b61dc17fc7f7f3d2f58373b30bc5274b6b87660a00b9078bada51f4e55eebe942b5c3d3f3e266e8fe5f748f13f7686e56f916bff004ce9dabae2577a0c39cf9dcde5b3c5998773ae7b6248d0e424b4947eb48ee50062b6983d3837bf6474e934c404845f3d39f7f0aaf983c7bc7fa34f7b36efd800fcf95e786badbdbdb7e6de7a4ad7ee2bcecdafac22bd63c6f5e0f52333b9fc0045a1d82e96f1901c2c3fcb7fd8e07ff6fc7e797b3e226b030cff51d0fca87c00f3c76a74c72939fe5696ffb81c3f37ec27e4f8bd2cffb41cbf8fe53f29c7df00c6f29f92e36f5f3b9b2b2e1666373df8eed28d237385dccca5d9f57f9f7e2b00ba589cc9cf14970e2c2ce40ac5837397e72bcef2403e77ba907d309ffb4cae00b0303337bbbcbc527ae164eef25c61e9c0c58b85dcc282e143462ed87026ab00f45736851fcd15cf6f5c55ba52cc5d29bed372272b10b82c46d1508a17a5f800504a1b4a6947291d28a513a5f8514a174ae946293d2865074ad9895276a194004fa0945b50caad28e53694723b4ab903a5e0b6b37bddb0ac067993fd67716e00e12d93135232af9d1f1ddb4fff2a46babccc27eabb1ca6529b2aa1572e9cf500b1354a9f59a631947e1853185a80a87a8230ae30b4108c2c0c2d0c630b431b0080d185a145607c6168511861185a0cc61886a6c328c3d0e230ce30b4048c34000c2d09630d434bc168c3d00661bc6168691871185a06c61c863604a30e431b00867187a18dc0c8c3d04661ec61686330fa30b471107f58da04f01196b6175600cf0c6dd22cf66edc33339b2d2c559ce4f4fcd386b15fadc4be8d1051b57ee000511ba9fe88b558077770aeb2077a0d3709306fabd6124c49e295f2e12b67ec00fba43d375d09e6e7be9c9db576cc6864edf8e2e5f9a92f193dd20e975e989a00bdb85188a053bec9091cd0db6ffcfb4f2f1d1fbb5cba76ae905d9f3818fcd500b9275b3d61d1c763df270f3b4ea6643e14f63b8ca2958abe32cf29e42c38c0000072820352138e163e201ae039d121d96adea99a400bec2003609253116be300661346f37c9f5b89301fe207220cb57e6d7a2e7bd13e7b0425b08480cdb0ed0005e16dcd6e8f4b8eb89d841b6e2761dc4e4275b29330a92b3ce587793d0e40008765c72dec60dc6cb0849d8d5b58b5a7da53ed396b2f54a7f6424a9f1f9df6009c8b046504c234e08629e286294a662f8429e6a625dd0d53dc0d53c20d535200a6dc4b1145cda05c5dd1255fd40ce2454d4ab6a8e1a6e578c7d272b23be43b0096c63b3658aa75972c5da77a2fc31b4a0ab6c245890cec85d237f2d7aaf4dd00547d6b4adf4dd5b757e9bba9faf6297d3755df6d4adf4dd577bbd27753f5dd00a1f4dd547d772a7d3755dffec6e81b119ab6136a4ed1e179a11f9be7854ecd0015730baba5e78fe5b2f3070a85ec12dc34c68fd40cae96ae6fdcbe625e588e002fb4fb6d8f5abcd3720b72c4e928dad85df839149412b0ef52d863fffb00f2007b04f93d8afc1e437ed791dfe3c8ef09e4f7a407d1de2154137b108ec34e250055cfc3ad0f1d3ba4cf9e1f953a1953d917e5ec05123d75a56e80f034b14519002a751289fe715d8bceb91bb7cb7cf463b4cbdc5fa7056e25b22122034a974a00a432222552199112a98c48e952895446a4442a23522295112991ca88942e95004865444ae4b63522fc211ff927de8853f141623fa0f6860c6147a8c3e2a93a00e92f056f63bad50b90ac3fcfb852f65c419bb53e110964189783e65391265b00d9fb4f43f8d7a967b19cbc00a4857fb0690ddda889c8be2b477aa326826fd4000cd469a3c6e674fd00ba511385d0384b8842ad3b3f971f258c2bea2084289100db4164e0a640a9ec52d9a5b24b2552d9a5b24b2552d9a51a716597ca2e95480065976ac4955d2abb5422955d2abb5476a9ec5289fc08db25f752de3aee3e820037fc0e70fb8fe04d80218e08200639620c0a415462f3fea618817400de76bd00c60db60ef1de676b1a052edefb8cdaec7dc6cade1543f830a7b2201c946dfc00bacc48c31f788b34fe75991152576ef751552455458312a98c48e95289544600a4442a23522295112991ca88942e954865444aa432222552199112a98c683b00ef04851afc649db1d5c22db4afa1db0eb1867fd022866f3b44ebb4ed1023c7001eff50668cd7a26e6a1169cce653273a312c4aa012b8cd04725f95ad63148a004117c4e350ed0d45d1fed8ec39bf872a11dd73de9471c866c7592f7b4f18a200ffc66d2aeb30f8b3444b3a20b6a363d4de7f943a35a0d72943ea5bd9deb6b1009cff62bd8bbbb19c78d9fbe9aa68ad85d36a7c2b558e895c3bfcd06236bf6000951ab7544b5c3fa365ad7df303ba655f643b596ba84eed855cab2e6289bc7600aadb65a84e6fb023189761fe6dc4756c282ce171da6ef9f34184c7c5cada8b0086e85efea3f078651c6c78651cc42be3409d2ae3206fd501b432b69c86a22c00bebf4e67a594482572db8b649ca4bf86c058c7f7dd10a5a35e272de8f0368e00da0f230a750a924fff0107580364e0a2b0061ce4987bd066c56750edea9d4000597bc2107e98ea30ffe9ef7ea83531f42934d4bb498fc1b2b664889ec644f700da1632fd94e0deb276da10fc08d769a0cf3eacd57ede04f6984d6203f86255000b7690f758d0db0de339a3f64af225816456c65f70d542a45e0ff16108ea9300f31abf8c695c7a89b3b73e7e3dc0b86c2316cfda591a981370e7c013e66527004b4b9a977efe5bbcc66517ff7927e3b29ba5a5cdcb1e96063e61b483a50d9900973b59dab079b98ba58d98979f6069a3e6e52d2c0dd8e6ad2c6ddcbcbc8da5004d9897b7b3b4bde6e51d2c6dd2bcbc93a5ed332f77b3b4fde6e55d2ced93780002ae9a3b9bc85aa04f6d385c756ac0dfc139cb1ee8372f6c34f8e1f7754ecf003f05fc68bd51db88b387cf1f7dd0fff0b0058af7beb5b3b9e26261967ae020002051960789ec1680b749877f32e45512c0a34687bf4d75a68faa390275aa390002f0364464982c0d10269b8fae87a19288be0de03616b6da5890ac98fa60d300a6ca9d9b8891c98ff17502c060560acd2ebcb88499904b98493e35600933050021e089a1573e20258c0a20ed3c240520323cb70f12b93d4de4f60c91bf878800fc3d4ce4e811ce3c123054b0c4a4831894725382a6cadab386cacf5955ae41007055ebdaa47965d767aa6d7c96b7a54d91e867850779a74809438ecd67102d0096c9f952dae24bc4842b454eb89212aeed28a404c0bc89d895d025b66763ce00424a4c7e2dd0b88cd8af06fec5e8cc4b4def0c171f3372363cc497ab587c1c0026e2e3484df131e3263e3a8e49f8bc619498378c11e9619c480f13c4bc612f009ef6339bf361dbf6ecaaac4c59fba5a1b6afa2e17353ecfa4715edd006eca300e79b40320a39c5414ed3902b827fe34e705228f8774e042739c1095a70b2ac00fdc189e0042738460b4e94b53f3b111ce304c769c19570f43610bc1998bcfb00a9c014aa53600a3988b271b2784798123c531c061ca26f09d9cdd618bdd9aa00bd6b687796da6cd52514107716ece37cb01f920bf6c37c48c782fd0811ec47006b0af6436e82fdb60de8f842d024660143574f2ee659a3332ed197308c9013007c846994671ab10c0563c1c35097ce810c99351fc234e606fd38cf64e9088b007e148ea80bf4531240c4e82778a6710afd18b43917e88f4b0011a3dfcb334d0050e8c7a157b8407f42028818fd24cfb497423fe1c06f29f4d31240c4e8f7f1004c93147ad0b57d6ed09f94002246bf9f67da47a1075ddbef06fda92d453f0400d1f315c370d9bb9bcfdb237279bba574ed5c213bbfb26a73e263042f1e87b900e27110da8c2dd85e233b3f84ae5cd8cd29d2703c79c983656f004846210f720090339611b7131c76273805bddf5670d4896072b2326e3b59f1c69d084e52a500ffa8ed64c59b7222384ecd82c6ec8f18668060a923864074d85ef4a83149d9002b5bab276005652b7cc2c05d04cde0e7378fda9d34038d98f329e75326470b006231f161b5385f37f63aa81b936ea2608a674ac211278639653bddf51e307400778cb6209939a0a8ea4c925bf77188dfd69f8ec0ad7be2ac630274cef9bccf009161c4c5e7867577869170631836d604c65ea7863669efa2670cdd7d57427700ba78ec9b6ef72ed04f490011a3b7dd5320d0276165e002fd71092062f4f4e600068f3e05ab0f17e84f480011a3cff04c690afd202c715ca09f960022463fc400336528f4695895b9407f52028818fdb0ddec80400fba36ec06fda92d45af4300f4dcd422de8cfd9f08b15c1822960b13c461a524715829451c561a240e2ba50089c34a19e2b0922ebfac69d6f48f10cb9ac458c6dc954548691ca796b12be500d86306dc9fa2fbe37ae3f6c713c2fd71db42a3d6f4cdd570295832a1e55523002b4fddbef2fc0e55795af637a8ba5417d756bacbba54074f42927b4b84c90700299397d942a2765062f036e927efc871aa54afcf181afe3beddcf579e4366600e79d20a43521f6c7b6c1debf2505de2c5b45849744d062cecdf1a74cd9fb220038fe84d689f4ac11cb0ab11ab3c2193c2b0c09b3c230b10edc2ab1783c0c0d0090f3b2117339c8f9324f868d9abcea861aa7ba8c1bd5656a545dcc4e75cf6100451415f36262e5651aa7bc98507943e449085785bdce2b6fd849aa22d742b600c06975a1f232648e733923e5943704c3bc73b78d8b95a7374e7971a1f2126400b5e35ce309d26d33300fd6d56de35be9b609376e2b5ae2e494978485c247c7006d136edc96565e9c54dee0c7dd6d93b26e9b84a526f59eb03898a6346ae7aa00354d7d962625f118cd0031034a596ee36adf70d9fbbe316e3f439ff7715356005744ff475c560fd8adc18b063e62a703808ad56b180e32a1f4709d941e86b70039ef43d8b41f02243af9891212a7a8d72045256c3c6c6e6610ef789199bf8700cd0d06374f03c40989d3d41a435c221685cd85786209262111a8c2e6e238b10049965c7720cb9a85657dae1a9fd78f96b037021b4c5efff05403774b1cde0d0065712b2561b95490e627f6d84a09a8a442fcfcd6b80c122b2c03c40a4b8458005d8f12abeb3162755d2756d7e3c4ea7a427eb527ec68951c8bda2e3394f16800a4e70af77a1ce34095690f9313b83d7cf0cdb7964effd1bf8b3f90550d9ed50067786b6ce87bf77ffef7dd911b930d6fe8b7edeffdebcd5f5c5a1637b43172007763a3a32139b595caa95ad93722cea91abf54d55a12edc579796bf10813b1008f67f2021c5c686b05ac124034e1fe419b1bf4ed3c531bec088bde0758dda0009f92002246dfc133b553e841d73adca03f2e01448cbe9367eaa0d083ae75ba00417f42028818bd9f67eaa4d083aef9dda09f96002246dfc533f929f4a06b5d006ed09fdc52f41a44cf643d0f0c4d0ccd12ab189a0f0e2d43f342a36568ed10001643d3a0ab3234e0bb9d754a64ef76dc7bb8f5e527820e12d98642377f6e3300970e6ccb9976f8a62296a7d5cc69d5dbcbbe2f54eb0ddf17ad458ecf6caa7a00c757903ba4dfdfe5b567e864193493c1d2b2dfbcc1f27b17b6d4b1f973b78d007a0c599d9c7abacbbe1966003a4cb6eac4936dbbc3be6d3fdb393fe66c5581002c4397c9501d91073043f449be40b14e76fdf2ee675efedf3fee7fbee195e00007bef213d30fbcfaeb8637b4f4e8e3bf7afed6d7f30d6fe8ecf5bed71effd6000f5e735edbb2b1d60f432b4bec82b11509eedd4855dc4555c51527f906389f008389e6abe22eb32ac65645fd35ae8a1ec557457b4aa255d11d7caaeb11e6c7009d3cd30ea0057eb56a27cc41e8948213da2d5e52ee699cf2fc42e575dbd5090022e5edb0eb27500fa7bc1d30f1bb9ad16c81e5696e94a70995d7432acfcb2b00afc7c174d0768a29549ed638e57985caf313905ba5cc15a887535e372c0f9d00bbad4fac3c6fe394e7132aaf8d802c331f6b23ddd65fe3541e579e6f2bddb600cd8ddb8a964138e5b53b5849b809ddb6cd8ddbd2caf391caeb9470db2e53790098c6c923b45d168be04a285fd9f72a7584d6431a04982a6b75aa457fb237fd00a99ecfe98f35bce8ed79e395d37f7d7f5e1736f47f26346d590ee8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0 \ No newline at end of file diff --git a/yarn-project/protocol-contracts/src/class-registry/__snapshots__/private_function_broadcasted_event.test.ts.snap b/yarn-project/protocol-contracts/src/class-registry/__snapshots__/private_function_broadcasted_event.test.ts.snap deleted file mode 100644 index 39d9fb3b0eaa..000000000000 --- a/yarn-project/protocol-contracts/src/class-registry/__snapshots__/private_function_broadcasted_event.test.ts.snap +++ /dev/null @@ -1,35 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`PrivateFunctionBroadcastedEvent parses an event as emitted by the ContractClassRegistry 1`] = ` -PrivateFunctionBroadcastedEvent { - "artifactFunctionTreeLeafIndex": 0, - "artifactFunctionTreeSiblingPath": [ - Fr<0x1405f27848f58d8b7bc2744c01f391c38558140988a50bec1e3ad27729fceb12>, - Fr<0x194f6d330fd4fea842e8bf23f950a18b7553b4104fdc5a0e298a59afa59fe8e9>, - Fr<0x07e5876f925d7c7b2dfd62320e14f44595d6989526de62bb4719538ce56191aa>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - ], - "artifactMetadataHash": Fr<0x059234bbc68b8b55e9c52abb3747b811297e35ab73813a96feb74640524fde22>, - "contractClassId": Fr<0x0806ce19f39561cf9bc58615c0541921fc90cae11394eb97f9371f02ec497807>, - "privateFunction": BroadcastedPrivateFunction { - "bytecode": Buffer<0x1f8b08000000000000ffed9d77601445ffff339fcf91d07b9516a44b51b07729a14a9162d7189203a221899704c11e7b3704b0778a58282a5810aca8a8fb164545c12862ef1d7bf96d48b9bd5c2e994bee8d3c7e7fcf3fcfb03bfbfacccececccece5c5e6ae19ceb3777494e4e3933d79f9a9c19484ecfccf50732533272929353b33273720379a9b95901f8fa14f94cfe8ac11929a9a70dce9a352c2f3375484a4646fea20983c60e4f2accbffb98f4dc4c7f4e8e245a64521b52331b528b232c32b576ceb7c8d5c62a57079b5275b4c9d4c92653679b4c895625ef62956b77ab5c5dad72754bccbf6f70203d23237d5af1f979710505730b0ad625c655fd3f937fefa09c1c7f20f7787f206b6ec19cc275897ba58d0d6c1b7047ef47c7273d9c9f7fec49bdf6fe6cc4ecc7b2e70cd9b67dee77ee25d0a55563dfecf7e16935c12e8b88d5b2442515b1727c568e3f3d2d2b73e0787f60465e6e4a6e7a5666e1bcf28a718b5b9eee5e9eeae139bf6c1e74397405f401e883a1252f2cacbe0a5b58dddd43d580cc51368fd9e51c117d09bb599570a5456bb129e1ca0a4dd1ccc95f3c313d735a86bfa4255417c4a624713b9833b233fcd055760ddda6e8ab4c68d1eb918bfe70f47d74ce5cab62b86cbb023f527dd3a859fc47aabfb39a9057b9e4b956ed799555ae47ac723d5a83a76451c2d27bb1b86bbb6769752f8f519e8bfbc457d98df3ab2de287f44289b2179aea0314c5350df6c2c783c935c1e4da60f28960f2c960f2a960f2e960f29960f2d960725d30f95c30f97c30f94230b93e987c31987c2976a3dcd376d9d6d9657bc12edb4b359a6abc5cf52d9f33e5a3336b827522627d65891a4d355ef6a41ff7a49ff0a41d77ba01e82bd00dd057434b6f3718acb1ca05ab9a78cda233d6a4265ef3a45ff1a43778d2afba35b111fa3af40de89bb5aef0b59ef4c60af3bb4dd0b7a06f4337d7a4b96ca9ba92fa1fd378544db0ef44c436aa55dd6ff1a49ff4a49ff2a4df712ba508fa2ef43de8d69ab4c2a7ad721559d5c4fb51be922c8bf88c55ae77ad8ab88d53c467ad72bd6755c40f487df97d4f7a9b27fd8127bdd56d4f1f423f827e0cfda42635b1ce2ad7875635f129e7613d6795eb23ab227ec629e2f356b93eb62ae2e7a4f6f4a927fd9927fdb927fd89db9ebe807e09fd0afa754d6ae205ab5c5f58d5c4379c87b5de2ad7975645fc9653c417ad727d6555c4ef48ede91b4ffa5b4ffa3b4ffa6bb73d7d0ffd01fa23f4a79ad4c44b56b9beb7aa89eda49ad8ee49ffe049ffe849ffe4d6c4cfd05fa0bf427fabc9bce5f7aa4b7fea0da350a3d2ffee49ffec496faa3099fb03fa27f42fe8dfa1df891af56acd1fd53f87c2e027d83fe5495f5c0d1657ed9ad91f568fe09ff05c2756c865dc5246fba5ed2b0cd66025f7573142b4d5fd4f348b633e13b32f5e9fa9e451d85572c57015e3bb6c1b964faaadcd38ab3b11773720fa5bf1c559e5b2bb150dbf958a1759dd8a56bab1b16c4c5e466efac4d4948c94809b9c37377fc990e2bdb194cc5c8bc6109e5736b438392f7ee149a9fd7a364afabe6df379171ebeeeea0b0eefd9370a6e9c7721e31fcf2dc44553b8b9f0f9e0ab035f7c25b7bd2269c6147f5a9a3f6d485e60a67f505a9a7be3c1383e4fba8e271defc9e4ae3c05d3cf7ad771a22c64027c75e1ab17becd505dd3303683802f21da898ad54bc957bf6a6ceb5f97f7acc94bc9573fb8ddeaa9d3e5a1af245f03f81ac2d708bec6d1efceb8cb435677d820dad13cfadd8b35510dd04d58bb176b5cb655817d4d29bb176efca605ecdaaefee56c3c75dd8c53d76eb36a66d546dd6c36b99a139e875bc6e6736ca3579369a73eb316b467d6c2ae3e5a909e86cd9c23ce7a29bbfa91af257de4b309e179b2ad48239fafa5cbb62b706bc6c8571cbf754df66dab7f864d5db2dd48d33466f71fd226ea14e62f1a1408a4cc2eb46b13ddabaf063b500f8bfa8cf23dbdbc7aa4e5cf7f42aa287e7ecc375aabfc9ef4fecfe9faccfb846a20fc7e233aa446b3c4e06b134cb68ddde76f9bc41acc4a7d6dacc6e676312a63bb0abf1a4a887a086f6b3384970c0135fd6d5c7557581522f43eebc6fec70d35b93bbb26d1ceaa49ec16a326b15b6d7f48665112ef5bbd7dec7a5cfb9a7cf63c065d6df7b1db813f21ea1055d575644d883ab86cbb0277a24c88dcf89da2fe14ac1f5c46b5fb2158d993afbe3c55b7e81dcb93b6afdc1d993def9ecec16462ec7a4267bb6c898935f8fd5c71b5cdb5abb6aabb5561c97a7967abce9768f108a26f689d8ac35bc5b72b65174a776cef82ed86a8dd6bf015516dfce25f193e6a17bf2be52bc6fefebb5156c6ec5f11dd6b316815d835c4ddacba5e57de88d52398ec19bb11ab875db69e1526d5f362596756c3550fab883d29c395db117ad86d3db5b7ca65772fbd6ad0a9ac3a750fbbd7487bab5c76f7d23bca01aad0eab9ecee361eab8c5ddd21ca6e24d98352d0dddcb25a65ece68e657605ed13edce92ddeecc2abb665e93e0b15844edc9086cb395d78b11582c02f76604568bc07b301a581fabdf222c8f36b4cde7445f4645fa2c02f76304ae6311b83f2370bc45e03d1981132c02efc5085cd722f00046e07a1681073202d7b708bc372370038bc0fb300237b408bc2f2370238bc0fb310237b608bc3f2370138bc0073002db6cb51dc8086cf36b828318819b5b043e9811b88545e04318815b5a043e9411b89545e0c318815b5b043e9c11b88d45e0231881db5a041ec408dcce22f06046609b7594218cc0364b004319813b58044e6204ee681178182370278bc0c319813b5b041ec1089c681178242370178bc0a3188177b7083c9a11b8ab45e023191fdd6318d0b18c958971562b130f319e4e378be28d67dcf35131fa8543f892a805d55de0b6cad8db5d94b569151328c5ec1e4531fbd8147322a3474c62402733a04733a0c730a0c732a0c731a0c733a02730a02732a02731a02733a0c90ce8290c680a033a85014d6540d318503f033a95019dc6804e6740d319d05319d0d318d00c067406039ac9806631a0d90ce8e90c688001cd61407319d03c067426037a06033a8b019dcd809ec9809ec5809ecd809ec3809ecb809ec7803ae753a8f914ea0514ea8514ea4514eac514ea2514eaa514ea6514eae514ea1514ea9514ea5514ead514ea3514eab5146a01853a87422da450e752a8f328d4f914ea7514eaf514ea0d14ea8d14ea4d14eacd14ea2d14eaad14ea6d14eaed14ea1d14ea9d14ea5d14ea020a752185ba88425d4ca1de4da12ea150efa150efa550efa350efa7509752a8cb2854cadf65382b28d40728d40729d48728d49514ea2a0af5610af5110af5510af5310a753585fa3885ba86425d4ba13e41a13e49a13e45a13e4da13e43a13e4ba1aea3509fa3509fa7505fa050d753a82f52a82f51a82f53a80e850a0af5150a750385fa2a85fa1a85ba91427d9d427d83427d9342dd44a1be45a1be4da16ea650b750a8ef50a84514eabb14ea7b14ea560af57d0a751b85fa0185fa2185fa1185fa3185fa0985fa2985fa1985fa3985fa0585fa2585fa1585fa3585fa0d85fa2d85fa1d85fa3d85fa0385fa2385fa1385ba9d42fd9942fd8542fd9542fd8d42fd9d42fd8342fd9342fd8b42fd9b42fd87418589e3600d072b1cac72b03e0eb60e071b1f2d3666ff253293f0ef85aecba9cc7a5597af6bd1a15da2bf23b50a5d9f51997dac4237e05466c39adc51f5d84685365685959ce08d6d82fbda702ab40907db94836dc6c136e7605b70b02d39d8561c6c6b0e96d41dda72b0ed38d8dd38d8f61c6c070eb62307db8983edccc12672b05d38d8dd39d8ae1c6c370eb63b07db8383edc9c1f6e2607b73b07b70b07d38d8be1c6c3f0eb63f07bb2707bb17073b80831dc8c1eecdc1eec3c1eecbc1eec7c1eecfc11ec0c11ec8c11ec4c11eccc11ec2c11ecac11ec6c11ecec11ec1c10ee2600773b04338d8a11c6c12073b8c831d1ee57fa9d2123b8253da911cec280e7634077b24073b86831dcbc18ee360c773b04771b01338d8891cec240e7632077b34077b0c077b2c077b1c077b3c077b02077b22077b12077b32079bccc19ec2c1a670b05338d8540e368d83f573b05339d8691cec740e369d833d95833d8d83cde0606770b0991c6c16079bcdc19ecec10638d81c0e369783cde3606772b06770b0b338d8d91cec991cec591cecd91cec391cecb91cec791cecf91c6c3e077b01077b21077b11077b31077b09077b29077b19077b39077b05077b25077b15077b35077b0d077b2d075bc0c1cee1600b39d8b91cec3c0e763e077b1d077b3d077b03077b23077b13077b33077b0b077b2b077b1b077b3b077b07077b27077b1707bb80835dc8c12ee2601773b07773b04b38d87b38d87b39d8fb38d8fb39d8a51cec320e763907bb82837d80837d90837d88835dc9c1aee2601fe6601fe1601fe5601fe3605773b08f73b06b38d8b51cec131cec931cec531cecd31cec331cecb31cec3a0ef6390ef6790ef6050e763d07fb2207fb1207fb3207eb70b0e0605fe1603770b0af72b0af71b01b39d8d739d83738d83739d84d1cec5b1cecdb1cec660e760b07fb0e075bc4c1becbc1bec7c16ee560dfe760b771b01f70b01f72b01f71b01f73b09f70b09f72b09f71b09f73b05f70b05f72b05f71b05f73b0df70b0df72b0df71b0df73b03f70b03f72b03f71b0db39d89f39d85f38d85f39d8df38d8df39d83f38d83f39d8bf38d8bf39588edb57386e5fe1b87d85e3f6158edb57386e5fe1b87d259e834de060394e5ea9c7c1d6e760392e5d69c8c1368a161b3383b334e6dc51939adc51f5d8a68536de5f8e3a5438265d69cec1724cbac231e90ac7a42b1c93ae704cbac231e90aa93b704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae0ce46039265de198748563d2158e495738265de198748563d2158e495738265de198748563d295c328ea50e198748563d2158e495738265de198748563d2158e495738265d19cec1724cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e9ca040e9663d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265d0970b01c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e94a2107cb31e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae3cc8c1724cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1c93ae704cbac231e90ac7a42b1b39588e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de19874e5070e9663d2158e495738265de198748563d2158e495738265de198748563d2158e495738265de198748563d2558e495739265de598749563d2558e495739265de598749563d2558e495739265de598749563d2558e49571b71b01c4bae36e1609b72b01cffad36e76039fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bfd5811c2cc77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb77a1807cbf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad4ee06039fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bf558eff5639fe5be5f86f95e3bfd50007cbf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad1672b01cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad72fcb7caf1df2ac77fab1cffad5af96fc7f8676405668fcc4ccf9ddbb8482fe9d9abf71e7dfaf6ebbfe75e0306eebdcfbefbed7fc081071d7cc8a1871d7ec4a0c14386260d1b3e62e4a8d1478e193b6efc5113264e9a7cf431c71e77fc09279e7472f229295352d3fc53a74d4f3ff5b48c199959d9a7077272f3669e316bf699679d7dceb9e739e73bf9ce05ce85ce45cec5ce25cea5ce65cee5ce15ce95ce55ced5ce35ceb54e8133c72974e63af39cf9ce75cef5ce0dce8dce4dcecdce2dceadce6dceedce1dce9dce5dce0267a1b3c859ecdced2c71ee71ee75ee73ee77963acb9ce5ce0ae701e741e72167a5b3ca79d879c479d479cc59ed3ceeac71d63a4f384f3a4f394f3bcf38cf3aeb9ce79ce79d179cf5ce8bce4bcecb8ee3c079c5d9e0bceabce66c745e77de70de7436396f396f3b9b9d2dce3b4e91f3aef39eb3d579dfd9e67ce07ce87ce47cec7ce27cea7ce67cee7ce17ce97ce57ced7ce37ceb7ce77ceffce0fce8fce46c777e767e717e757e737e77fe70fe74fe72fe76fe818983313002a3303e983a30f13009307561eac1d4876900d310a6114c639826304d619ac134876901d312a6154c6b9836306d61dac1ec06d31ea6034c47984e309d611261bac0ec0ed315a61b4c77981e303d617ac1f486d903a60f4c5f987e30fd61f684d90b6600cc4098bd61f681d917663f98fd610e803910e6209883610e813914e63098c3618e80190433186608cc509824986130c36146c08c841905331ae6489831306361c6c18c87390a6602cc4498493093618e863906e65898e3608e873901e6449893604e86498639052605660a4c2a4c1a8c1f662acc3498e930e930a7c29c06930133032613260b261be67498004c0e4c2e4c1ecc4c98336066c1cc863913e62c98b361ce813917e63c98f361f2612e80b910e622988b612e81b914e63298cb61ae80b912e62a98ab61ae81b916a600660e4c21cc5c987930f361ae83b91ee606981b616e82b919e616985b616e83b91de60e983b61ee825900b3106611cc6298bb6196c0dc03732fcc7d30f7c32c855906b31c6605cc03300fc23c04b3126615ccc3308fc03c0af318cc6a98c761d6c0ac857902e64998a7609e867906e659987530cfc13c0ff302cc7a9817615e827919c68101cc2b301b605e85790d6623cceb306fc0bc09b309e62d98b76136c36c817907a608e65d98f760b6c2bc0fb30de603980f613e82f918e613984f613e83f91ce60b982f61be82f91ae61b986f61be83f91ee607981f617e82d90ef333cc2f30bfc2fc06f33bcc1f307fc2fc05f337cc3f903888810844213e481d483c24015217520f521fd200d210d208d218d204d214d20cd21cd202d212d20ad21ad206d216d20eb21ba43da403a423a413a4332411d205b23ba42ba41ba43ba407a427a417a437640f481f485f483f487fc89e90bd20032003217b43f681ec0bd90fb23fe400c88190832007430e811c0a390c7238e408c820c860c810c8504812641864386404642464146434e448c818c858c838c878c85190099089904990c990a321c7408e851c07391e7202e444c849909321c99053202990299054481ac40f990a9906990e49879c0a390d92019901c9846441b221a74302901c482e240f3213720664166436e44cc85990b321e740ce859c07391f920fb9007221e422c8c5904b2097422e835c0eb9027225e42ac8d5906b20d7420a2073208590b9907990f990eb20d7436e80dc08b9097233e416c8ad90db20b743ee80dc09b90bb200b210b208b21872376409e41ec8bd90fb20f74396429641964356401e803c087908b212b20af230e411c8a390c720ab218f43d640d6429e803c09790af234e419c8b3907590e720cf435e80ac87bc087909f232c48100f20a6403e455c86b908d90d7216f40de846c82bc05791bb219b205f20ea408f22ee43dc856c8fb906d900f201f423e827c0cf904f229e433c8e7902f205f42be827c0df906f22de43bc8f7901f203f427e826c87fc0cf905f22be437c8ef903f207f42fe82fc0df9071a07755fca0255a80f5a071a0f4d80d685d683d68736803684368236863681368536833687b680b684b682b686b681b685b683ee066d0fed00ed08ed04ed0c4d847681ee0eed0aed06ed0eed01ed09ed05ed0ddd03da07da17da0fda1fba27742fe800e840e8ded07da0fb42f783ee0f3d007a20f420e8c1d043a087420f831e0e3d023a083a183a043a149a041d061d0e1d011d091d051d0d3d123a063a163a0e3a1e7a147402742274127432f468e831d063a1c7418f879e003d117a12f4646832f414680a740a34159a06f543a742a7b95bfcee76bcbb75ee6e73bb5bd2eef6b1bbd5eb6ecbba5ba8ee76a7bb35e96e23ba5b7eeef69cbb95e66e7bb95b54ee7692bbf5e36ed3b85b2aeef687bb55e16e2bb85b00ee72bdbbb4ee2e83bb4bd6eef2b2bb14ec2edbba4bacee72a8bb74e92e33ba4b82eef29dbbd4e62e8bb94b58ee7293bb34e42ee3b84b2eeef288bb94e12e3bb84b04eee7bcfbe9ed7e26bb9fb4eee7a7fba9e87ed6b99f60eee792fb69e37e86b89f0ceef4de9d8abbd366778aeb4e47dda9a33bcd73a764f74ef0e7e6053287a6e4a614c5f58c33a2be3af10975ebd56fd0b051e3264d9b356fd1b255eb366ddbedd6be43c74e9d13bbecdeb55bf71e05053715e62f1c949a1e6859b8e1d5842f7f7af9f9690505a5875a871f1a5cb86169838d83d7de5ef7e4b24343c30f8d2adc70c5e483faf699b6fedbb243c7146e58df282727132bb7951d3a311c7f72e186e95b3e4f76de9b7977d9a194f05cb30b37f438e028d33e77d3d6a2b893f2ef4d9a951df0e7e4a46765ce2da87e75707cb4174c8ff68294682fc889f6027fb41724467b41eaae574b99d15e306dd7abd6347a9172e9115277bd22a5d11f5c16bdb5465da4a9d15e9047bf077ed348ff0f3cb833e8379d4b1fbda3bee9ecffffdadd255ebbfce1feb4682fe84fafd691f4b13571d7eba2e9f4fe90f81f2852d4fd61167de4fb3f3923eb4b7fd279f4c6c77f3f748df68213e8d39f2c7a7ff82fcc04fe4fbed8a35e9b89bbb97073af941ddb91c9a95933b25372d3a764f893b30229a9eeffcdf4078a41c9670452b2b3fd81a2b8d6f98b866465e6e4cecd5f3c343de04fcd95fcbb4766e6faa7f9030b26ef3db0fa9dcc8ad79ba8ae3f7f68c5ebe3a28b3f347fe190948c8c390dca394b26f833dc9b9ee98fae24ee766b1841a225dc5f5c963477f9704856f6ecf25b1aea2d93075e52f246b52ef9d018947ce1c4dcacec3985114a5ae1190d59342cdd9f51fd5fce35585cb2a05a7aa74df3ef1b9615f0a74fcb2cfee7fccdddf272d333d27367976c9e0f296face376b4d5a34b9a6a41c19cfca525fbe083d2d28a3b427929e6e42f9e983e233bc35f529cb260150aeb8baa2a660dcdbf6f707a664af1ae7beeb8ecf965145d72a41b7ad2f494cc624ab0b19607593c2a6f46f6c8a985e517b4ca5f3a3233ada4a4117bc87e55e88cb63cb77dd3ca510367e42f9ce4f6d63985c1ebcbba6ae91d176ede2b3d2739273dcd9fec9f3ad53de176fc3c37422039e0773b7cc80050def1dbfdcb1d7f582d3bfeb0d246d5b8d68ddf841334e61d5fbc70b7e49e1b1f1a4c78a3e62f18933533a44396672bb9f326a5394a0f2779b3d6b64e926a5d27267c48f1d641e8c8d0a2e2c8901d489f9992eb1f9933d16dd5493b1af59092363da1bc49878d0bc140e523437999ef9a3c20727e139ebff227108c109bc16658ac069bb63b6fb079629abf7880c9cc750794dce47437584a66aadf4db80f273325a3286ebf7f7964995ccb91657269a3ecf03f38b254376674ac62cc083933cc1b20e4ccf0f078a5674604cff842cf8c0c9ea9137a6654f04c7ce899d1c13309a1678e0c9ea91b7a664cf04cbdd033638367ea879e19173cd320f4ccf8e09986a1678e0a9e69147a6642f04ce3d0331383672a8cdc9382679a86b79866b51eb99b4747681a3e7237f3c04247eebd4247eed5a553bae1fee27168c71031b274842872c11107ec886734e2195fc43375229e898f782621e299ba11cfd48b78a67ec4330d229e6918f14ca388671a473cd324e299c84fa159f1230a9952c7f65fde97f1ae96df6672507a7515b3828ae7aa1a9b357cb4f41c081b2f3d07c2464ccf81b031d373206cd4f41c081b373d07c2464ecf81b0b1d373206cf4f41c081b3f3d07c24650cf81b031d473c0d3c42b9c6bea69e4b199b94d8ed5cc6ddf9d37735b96e62f5e11cacaf1274f77a76b45719dfee599daf05aced486ff0f7f03d6e6eb2ef24cadb67751c9ec21ba8e1127e1b307ef48173a7b685bf6295c59de60635a3879c0c003c2b27aabb374685e5ad22777fc635cf63c4f860513f3a64418b3c397dfca96b75a0c887babf3d67d67f769bd5fd6b899176d9db4f4dc960b7a7fdaa4ed377987ccfcad282b723cdf8231791911eeaa66ef0f5f6cc6ade1b11ab73aeebc716b49466ed988d5e5bf3b6269b4fdacb63dd562c4aa6a8ee219b1c25a6df9a055e958362c962b55c376ee4a55fb2afafce2a4d3f35232722274edf21e14bca075fec2e283eef257646879c7b41b31429e5809bd5d551b0ceed4d7a282220731e541220f6bba8b0d5d893b7165be674eaefb79ecae8e25fb67f953ddaf6377f32d3525757af8827cffffc8d0d6b6d61d5277fad046d8818b7ada14d6c7ca1f6318bb4eb4f55969c7310bdc6f5d6f9f31214b2815ba48cd47d7d0e0c110e5e1c3efb9c292a3469cfc56f2a15c7aa64ec840b8a365563946457c89956d3475ab752b19cedea72d1b672b7ddc09151fb706ab2aa4e6ea0633841caf17acf44a03d42f7dff796394b312c286e1fa5df3171e999592567e203e78d1a289c5a36678e4f8ca23d7ad786b75830da9d20bea55bca05ef08292f75987d0d77eaf48ad58c35bb1f104f7ae8dae29ddd4da716f233393cade07438a5f07c57f9973cf087f4af6a0402065b6771c88bc362a85f98b4ab25758a212c6c678cc5ebffd76e25e55aa5bf1c9a5f59e3c352f3375c70b38bd7caf6acf7ff9a53bba962fddd1a5edb355f8d0121f1da94e382121e62fdd90f5c9d05df0a46022640f3634d7f060a28a5c238209ef92704d3e6022bd9587852fd996972c6cc1b6bc34250fab75c4cd31137173acf64b3b2377eed24eeff0b5e9c8139a04fa842621f284263e46139a84f057417c8457c1ead221a9f81fe34b92c34a07a73995bf0612644ea4c17e7ee4ad8b9db5e136bf9a0d969287629d25a4d7d676d665a27875c7877cd896cc4e9654ba7c5bc5baa5543a34553ae92cad8b2a5636436fb6d69521912ac317656554bd32b4c466453346cb02a363352fe9bff3e6250b33dcda2c8aebf72fcf3e926a39fb482afff9d67f6fffa565a49e2255cef8fffd0d98b888dfce12f1db59abfd76aefd8fa19262f088a358a8ed19715267ac670fe54f35e2dc4178ef22893077585b3a77281e908705b266847e4746983e685ca4e9435c6c7ef9f0bfb5f4d063575e7ae81c9bf76252acde8b7d77e27279f7ccacdcf4a9b39353037eb789a72567e66564a44f4df707ca96cbb30359b366fffb7fb612ab3767c3ffe09bb3f67fd092b4b3164a2bfdbb94b20176ec8ea638a4a4258e2d6b8895fc3d4ac4017317ebc83bf12f5256948471ebc45d6c2bfec1fe8d152ba1552d3b6ccbd834f2b86079cac1158716cbbf768a0bdbc52dabf8b28de18a3125fc19d50d1b982ca39b48d1e3160c4d9f19f6a4821da4fcb6cb2aa270b5f7e1eda8e2e4d3f3dccee0cfccbda162f1ead7f4570ba5d73788f163ac1f0447a80fb9b734a0a75ae282f513e12ab3e3073dc1e7566df6e2df1b85d34366359e7650e1613428bf9dff077cec6cb0ccc60100>, - "metadataHash": Fr<0x1eef7f2eaafa09e24b0475a918bd7388c4dec305145849f5f84d1b822202b944>, - "selector": Selector<0x1c97a6b2>, - "vkHash": Fr<0x028ad70b98d31f63fa89c5151b51d8ebefedd049e6ade196431690630d5fbd6c>, - }, - "privateFunctionTreeLeafIndex": 0, - "privateFunctionTreeSiblingPath": [ - Fr<0x2fd363c29efb017bd7ae341748dd643c73bc19a714b4a288ce5930b64547bf4f>, - Fr<0x1901f8aabbbfc15c27140e1dd356997345e5a992bf9c641e767b55b95c2d1c15>, - Fr<0x01a83cb29c25febaf05115f7802daf31444e3150a1208117178b5b0c4abb5dce>, - Fr<0x2373ea368857ec7af97e7b470d705848e2bf93ed7bef142a490f2119bcf82d8e>, - Fr<0x120157cfaaa49ce3da30f8b47879114977c24b266d58b0ac18b325d878aafddf>, - Fr<0x01c28fe1059ae0237b72334700697bdf465e03df03986fe05200cadeda66bd76>, - Fr<0x2d78ed82f93b61ba718b17c2dfe5b52375b4d37cbbed6f1fc98b47614b0cf21b>, - ], - "utilityFunctionsTreeRoot": Fr<0x0d3856d35260777dde79a3415d94a80d95192d933225ca4d3cbcb12613de9f46>, -} -`; diff --git a/yarn-project/protocol-contracts/src/class-registry/__snapshots__/utility_function_broadcasted_event.test.ts.snap b/yarn-project/protocol-contracts/src/class-registry/__snapshots__/utility_function_broadcasted_event.test.ts.snap deleted file mode 100644 index 47d2eccc3b4e..000000000000 --- a/yarn-project/protocol-contracts/src/class-registry/__snapshots__/utility_function_broadcasted_event.test.ts.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`UtilityFunctionBroadcastedEvent parses an event as emitted by the ContractClassRegistry 1`] = ` -UtilityFunctionBroadcastedEvent { - "artifactFunctionTreeLeafIndex": 0, - "artifactFunctionTreeSiblingPath": [ - Fr<0x2d1eb762c990e1d198f3bd6ea7ded6f6bba8d8a042dee7ae71b76dc816575049>, - Fr<0x005e3c4cbf3aaaf95376a77f30baf1dcc21581dda5d1eeb8c464aebf2fdbca0d>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - ], - "artifactMetadataHash": Fr<0x059234bbc68b8b55e9c52abb3747b811297e35ab73813a96feb74640524fde22>, - "contractClassId": Fr<0x0806ce19f39561cf9bc58615c0541921fc90cae11394eb97f9371f02ec497807>, - "privateFunctionsArtifactTreeRoot": Fr<0x0193cc7bba584f0fb2210f2e762185511bd95a9ade5e06e0e45c3e057128dfd0>, - "utilityFunction": BroadcastedUtilityFunction { - "bytecode": Buffer<0x1f8b08000000000000ffed5d6b6c1c5715f67a76fd58db49e8338ded7d7877671fdef53b4e803f4df374e2244d9af01042e9b659826163bbeb751b8350bbfca84054c8765b50257e358969155a7ed04aad84848a5a84288b40bcd4d23f48fd57098100a91245b0aebd3367ee9d73eedcd9879df6fad778cf9c73bf7bee79ddc7cc68ab2bdf7f2b72e142f66bc5dc8317660b1766668bb9c26c36bf70e1c2c2e2e5cbb98b171ecee617730b9e95d28d7b0a33f9fccca583d97cfea99695d2da7d33b397f2b9279757565f0fb5d07f9e16e12d1b122fcfe7739e279797c512575a96573c95b61dc17fc7f7f3d2f58373b30bc5274b6b87660ab9078bada51f4e55eebe942b5c3d3f3e266e8fe5f748f13f7686e56f916bff4ce9dabae2577a0c39cf9dcde5b3c5998773ae7b6248d0e424b4947eb48ee562b6983d3837bf6474e934c404845f3d39f7f0aaf983c7bc7fa34f7b36efd8fcf95e786badbdbdb7e6de7a4ad7ee2bcecdafac22bd63c6f5e0f52333b9fc45a1d82e96f1901c2c3fcb7fd8e07ff6fc7e797b3e226b030cff51d0fca87cf3c76a74c72939fe5696ffb81c3f37ec27e4f8bd2cffb41cbf8fe53f29c7dfc6f29f92e36f5f3b9b2b2e1666373df8eed28d237385dccca5d9f57f9f7e2bba589cc9cf14970e2c2ce40ac5837397e72bcef2403e77ba907d309ffb4caeb0303337bbbcbc527ae164eef25c61e9c0c58b85dcc282e143462ed87026abf45736851fcd15cf6f5c55ba52cc5d29bed372272b10b82c46d1508a17a5f8504a1b4a6947291d28a513a5f8514a174ae946293d2865074ad9895276a1944fa0945b50caad28e53694723b4ab903a5e0b6b37bddb0ac067993fd67716ee12d93135232af9d1f1ddb4fff2a46babccc27eabb1ca6529b2aa1572e9cf5b1354a9f59a631947e1853185a80a87a8230ae30b4108c2c0c2d0c630b431b80d185a145607c6168511861185a0cc61886a6c328c3d0e230ce30b4048c340c2d09630d434bc168c3d00661bc6168691871185a06c61c863604a30e431b867187a18dc0c8c3d04661ec61686330fa30b471107f58da04f01196b61756cf0c6dd22cf66edc33339b2d2c559ce4f4fcd386b15fadc4be8d1051b57ee0511ba9fe88b558077770aeb2077a0d3709306fabd6124c49e295f2e12b67ecfba43d375d09e6e7be9c9db576cc6864edf8e2e5f9a92f193dd20e975e989abdb85188a053bec9091cd0db6ffcfb4f2f1d1fbb5cba76ae905d9f3818fcd5b9275b3d61d1c763df270f3b4ea6643e14f63b8ca2958abe32cf29e42c38c00072820352138e163e201ae039d121d96adea99a400bec2003609253116be3661346f37c9f5b89301fe207220cb57e6d7a2e7bd13e7b0425b08480cdb0ed05e16dcd6e8f4b8eb89d841b6e2761dc4e4275b29330a92b3ce587793d0e408765c72dec60dc6cb0849d8d5b58b5a7da53ed396b2f54a7f6424a9f1f9df69c8b046504c234e08629e286294a662f8429e6a625dd0d53dc0d53c20d5352a6dc4b1145cda05c5dd1255fd40ce2454d4ab6a8e1a6e578c7d272b23be43b96c63b3658aa75972c5da77a2fc31b4a0ab6c245890cec85d237f2d7aaf4dd547d6b4adf4dd5b757e9bba9faf6297d3755df6d4adf4dd577bbd27753f5dda1f4dd547d772a7d3755dffec6e81b119ab6136a4ed1e179a11f9be7854ecd15730baba5e78fe5b2f3070a85ec12dc34c68fd40cae96ae6fdcbe625e588e2fb4fb6d8f5abcd3720b72c4e928dad85df839149412b0ef52d863fffb00f27b04f93d8afc1e437ed791dfe3c8ef09e4f7a407d1de2154137b108ec34e2555cfc3ad0f1d3ba4cf9e1f953a1953d917e5ec05123d75a56e80f034b145192a751289fe715d8bceb91bb7cb7cf463b4cbdc5fa7056e25b22122034a974aa432222552199112a98c48e952895446a4442a23522295112991ca88942e954865444ae4b63522fc211ff927de8853f141623fa0f6860c6147a8c3e2a93ae92f056f63bad50b90ac3fcfb852f65c419bb53e110964189783e65391265bd9fb4f43f8d7a967b19cbc00a4857fb0690ddda889c8be2b477aa326826fd40cd469a3c6e674fd00ba511385d0384b8842ad3b3f971f258c2bea20842891db4164e0a640a9ec52d9a5b24b2552d9a5b24b2552d9a51a716597ca2e954865976ac4955d2abb5422955d2abb5476a9ec5289fc08db25f752de3aee3e8237fc0e70fb8fe04d80218e08200639620c0a415462f3fea618817400de76bdc60db60ef1de676b1a052edefb8cdaec7dc6cade1543f830a7b2201c946dfcbacc48c31f788b34fe75991152576ef751552455458312a98c48e952895446a4442a23522295112991ca88942e954865444aa432222552199112a98c683bef04851afc649db1d5c22db4afa1db0eb1867fd022866f3b44ebb4ed1023c71eff50668cd7a26e6a1169cce653273a312c4aa012b8cd04725f95ad63148a4117c4e350ed0d45d1fed8ec39bf872a11dd73de9471c866c7592f7b4f18a2ffc66d2aeb30f8b3444b3a20b6a363d4de7f943a35a0d72943ea5bd9deb6b19cff62bd8bbbb19c78d9fbe9aa68ad85d36a7c2b558e895c3bfcd06236bf60951ab7544b5c3fa365ad7df303ba655f643b596ba84eed855cab2e6289bc76aadb65a84e6fb023189761fe6dc4756c282ce171da6ef9f34184c7c5cada8b86e85efea3f078651c6c78651cc42be3409d2ae3206fd501b432b69c86a22cbebf4e67a594482572db8b649ca4bf86c058c7f7dd10a5a35e272de8f0368eda0f230a750a924fff0107580364e0a2b0061ce4987bd066c56750edea9d40597bc2107e98ea30ffe9ef7ea83531f42934d4bb498fc1b2b664889ec644f7da1632fd94e0deb276da10fc08d769a0cf3eacd57ede04f6984d6203f862550b7690f758d0db0de339a3f64af225816456c65f70d542a45e0ff16108ea93f31abf8c695c7a89b3b73e7e3dc0b86c2316cfda591a981370e7c013e665274b4b9a977efe5bbcc66517ff7927e3b29ba5a5cdcb1e96063e61b483a50d99973b59dab079b98ba58d98979f6069a3e6e52d2c0dd8e6ad2c6ddcbcbc8da54d9897b7b3b4bde6e51d2c6dd2bcbc93a5ed332f77b3b4fde6e55d2ced937802ae9a3b9bc85aa04f6d385c756ac0dfc139cb1ee8372f6c34f8e1f7754ecf3f05fc68bd51db88b387cf1f7dd0fff0b0058af7beb5b3b9e26261967ae0202051960789ec1680b749877f32e45512c0a34687bf4d75a68faa390275aa3902f0364464982c0d10269b8fae87a19288be0de03616b6da5890ac98fa60d3a6ca9d9b8891c98ff17502c060560acd2ebcb88499904b98493e356009330521e089a1573e20258c0a20ed3c240520323cb70f12b93d4de4f60c91bf8788fc3d4ce4e811ce3c123054b0c4a4831894725382a6cadab386cacf5955ae417055ebdaa47965d767aa6d7c96b7a54d91e867850779a74809438ecd67102d96c9f952dae24bc4842b454eb89212aeed28a404c0bc89d895d025b66763ce424a4c7e2dd0b88cd8af06fec5e8cc4b4def0c171f3372363cc497ab587c1c26e2e3484df131e3263e3a8e49f8bc619498378c11e9619c480f13c4bc612f9ef6339bf361dbf6ecaaac4c59fba5a1b6afa2e17353ecfa4715edd006eca3e79b40320a39c5414ed3902b827fe34e705228f8774e042739c1095a70b2acfdc189e0042738460b4e94b53f3b111ce304c769c19570f43610bc1998bcfba9c014aa53600a3988b271b2784798123c531c061ca26f09d9cdd618bdd9aabd6b687796da6cd52514107716ece37cb01f920bf6c37c48c782fd0811ec476b0af6436e82fdb60de8f842d024660143574f2ee659a3332ed197308c90137c846994671ab10c0563c1c35097ce810c99351fc234e606fd38cf64e9088b7e148ea80bf4531240c4e82778a6710afd18b43917e88f4b0011a3dfcb334d50e8c7a157b8407f42028818fd24cfb497423fe1c06f29f4d31240c4e8f7f14c93147ad0b57d6ed09f94002246bf9f67da47a1075ddbef06fda92d453f04d1f315c370d9bb9bcfdb237279bba574ed5c213bbfb26a73e263042f1e87b9e27110da8c2dd85e233b3f84ae5cd8cd29d2703c79c983656f004846210f7290339611b7131c76273805bddf5670d4896072b2326e3b59f1c69d084e52a5ffa8ed64c59b7222384ecd82c6ec8f18668060a923864074d85ef4a83149d92b5bab276005652b7cc2c05d04cde0e7378fda9d34038d98f329e75326470b6231f161b5385f37f63aa81b936ea2608a674ac211278639653bddf51e3074778cb6209939a0a8ea4c925bf77188dfd69f8ec0ad7be2ac630274cef9bccf9161c4c5e7867577869170631836d604c65ea7863669efa2670cdd7d574277ba78ec9b6ef72ed04f490011a3b7dd5320d0276165e002fd71092062f4f4e6068f3e05ab0f17e84f480011a3cff04c690afd202c715ca09f960022463fc4336528f4695895b9407f52028818fdb0ddec80400fba36ec06fda92d45af43f4dcd422de8cfd9f08b15c1822960b13c461a524715829451c561a240e2ba589c34a19e2b0922ebfac69d6f48f10cb9ac458c6dc954548691ca796b12be5d86306dc9fa2fbe37ae3f6c713c2fd71db42a3d6f4cdd570295832a1e555232b4fddbef2fc0e55795af637a8ba5417d756bacbba54074f42927b4b84c907299397d942a2765062f036e927efc871aa54afcf181afe3beddcf579e43666e79d20a43521f6c7b6c1debf2505de2c5b45849744d062cecdf1a74cd9fb2238fe84d689f4ac11cb0ab11ab3c2193c2b0c09b3c230b10edc2ab1783c0c0d90f3b2117339c8f9324f868d9abcea861aa7ba8c1bd5656a545dcc4e75cf61451415f36262e5651aa7bc98507943e449085785bdce2b6fd849aa22d742b6c06975a1f232648e733923e5943704c3bc73b78d8b95a7374e7971a1f21264b5e35ce309d26d33300fd6d56de35be9b609376e2b5ae2e494978485c247c76d136edc96565e9c54dee0c7dd6d93b26e9b84a526f59eb03898a6346ae7aa354d7d962625f118cd0031034a596ee36adf70d9fbbe316e3f439ff77153565744ff475c560fd8adc18b063e62a703808ad56b180e32a1f4709d941e86b739ef43d8b41f02243af9891212a7a8d72045256c3c6c6e6610ef789199bf87cd0d06374f03c40989d3d41a435c221685cd85786209262111a8c2e6e238b149965c7720cb9a85657dae1a9fd78f96b037021b4c5efff05403774b1cde0d65712b2561b95490e627f6d84a09a8a442fcfcd6b80c122b2c03c40a4b84585d8f12abeb3162755d2756d7e3c4ea7a427eb527ec68951c8bda2e3394f168a4e70af77a1ce34095690f9313b83d7cf0cdb7964effd1bf8b3f90550d9ed567786b6ce87bf77ffef7dd911b930d6fe8b7edeffdebcd5f5c5a1637b431727763a3a32139b595caa95ad93722cea91abf54d55a12edc579796bf10813b18f67f2021c5c686b05ac124034e1fe419b1bf4ed3c531bec088bde0758dda09f92002246dfc133b553e841d73adca03f2e01448cbe9367eaa0d083ae75ba417f42028818bd9f67eaa4d083aef9dda09f96002246dfc533f929f4a06b5d6ed09fdc52f41a44cf643d0f0c4d0ccd12ab189a0f0e2d43f342a36568ed101643d3a0ab3234e0bb9d754a64ef76dc7bb8f5e527820e12d98642377f6e33970e6ccb9976f8a62296a7d5cc69d5dbcbbe2f54eb0ddf17ad458ecf6caa7ac757903ba4dfdfe5b567e864193493c1d2b2dfbcc1f27b17b6d4b1f973b78d7a0c599d9c7abacbbe1966003a4cb6eac4936dbbc3be6d3fdb393fe66c55812c4397c9501d91073043f449be40b14e76fdf2ee675efedf3fee7fbee195e007bef213d30fbcfaeb8637b4f4e8e3bf7afed6d7f30d6fe8ecf5bed71effd60f5e735edbb2b1d60f432b4bec82b11509eedd4855dc4555c51527f906389f8389e6abe22eb32ac65645fd35ae8a1ec557457b4aa255d11d7caaeb11e6c79d3cd30ea0057eb56a27cc41e8948213da2d5e52ee699cf2fc42e575dbd50922e5edb0eb27500fa7bc1d30f1bb9ad16c81e5696e94a70995d7432acfcb2bafc7c174d0768a29549ed638e57985caf313905ba5cc15a887535e372c0f9dbbad4fac3c6fe394e7132aaf8d802c331f6b23ddd65fe3541e579e6f2bddb6cd8ddb8a964138e5b53b5849b809ddb6cd8ddbd2caf391caeb9470db2e537998c6c923b45d168be04a285fd9f72a7584d6431a04982a6b75aa457fb237fda99ecfe98f35bce8ed79e395d37f7d7f5e1736f47f26346d590ee80000>, - "metadataHash": Fr<0x1125672f40d49b4b192a803a41ccb3ddacec4da05bf7d535224059fa9bf766cd>, - "selector": Selector<0x111cfcfc>, - }, -} -`; diff --git a/yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.test.ts b/yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.test.ts index 860533d58fe0..8647fc333f26 100644 --- a/yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.test.ts +++ b/yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.test.ts @@ -1,11 +1,20 @@ +import { + CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, + CONTRACT_CLASS_PUBLISHED_MAGIC_VALUE, + MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, +} from '@aztec/constants'; +import { Fr } from '@aztec/foundation/curves/bn254'; import { setupCustomSnapshotSerializers } from '@aztec/foundation/testing'; -import { ContractClassLog } from '@aztec/stdlib/logs'; +import { bufferAsFields } from '@aztec/stdlib/abi'; +import { ContractClassLog, ContractClassLogFields } from '@aztec/stdlib/logs'; +import { ProtocolContractAddress } from '../protocol_contract_data.js'; import { getSampleContractClassPublishedEventPayload } from '../tests/fixtures.js'; import { ContractClassPublishedEvent } from './contract_class_published_event.js'; describe('ContractClassPublishedEvent', () => { beforeAll(() => setupCustomSnapshotSerializers(expect)); + it('parses an event as emitted by the ContractClassRegistry', () => { const log = ContractClassLog.fromBuffer(getSampleContractClassPublishedEventPayload()); expect(ContractClassPublishedEvent.isContractClassPublishedEvent(log)).toBe(true); @@ -15,4 +24,42 @@ describe('ContractClassPublishedEvent', () => { // See ./__snapshots__/README.md for how to update the snapshot. expect(event).toMatchSnapshot(); }); + + it('fits a max-size public bytecode within CONTRACT_CLASS_LOG_SIZE_IN_FIELDS', () => { + // Create a bytecode that fills the maximum allowed size. + // bufferAsFields encodes as [length_in_bytes, ...31-byte-chunks, ...zero-padding] up to the target length. + // The max bytecode in bytes is (MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS - 1) * 31, + // since one field is used for the length prefix. + const maxBytecodeBytes = (MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS - 1) * 31; + const maxBytecode = Buffer.alloc(maxBytecodeBytes, 0xab); + + // Encode the bytecode as fields (same encoding used in the Noir contract). + const bytecodeFields = bufferAsFields(maxBytecode, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS); + expect(bytecodeFields).toHaveLength(MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS); + + // The event header: [magic, contractClassId, version, artifactHash, privateFunctionsRoot] + const headerFields = [ + new Fr(CONTRACT_CLASS_PUBLISHED_MAGIC_VALUE), + Fr.random(), // contractClassId + new Fr(1), // version + Fr.random(), // artifactHash + Fr.random(), // privateFunctionsRoot + ]; + + // This is the main assertion: the CONTRACT_CLASS_LOG_SIZE_IN_FIELDS is enough such that + // a max-size bytecode can be included in the event log together with the header fields + const totalFields = headerFields.length + bytecodeFields.length; + expect(totalFields).toBeLessThanOrEqual(CONTRACT_CLASS_LOG_SIZE_IN_FIELDS); + + // Verify it round-trips through ContractClassLog and ContractClassPublishedEvent. + const allFields = [...headerFields, ...bytecodeFields]; + const padded = [...allFields, ...Array(CONTRACT_CLASS_LOG_SIZE_IN_FIELDS - allFields.length).fill(Fr.ZERO)]; + const logFields = new ContractClassLogFields(padded); + const log = new ContractClassLog(ProtocolContractAddress.ContractClassRegistry, logFields, allFields.length); + + expect(ContractClassPublishedEvent.isContractClassPublishedEvent(log)).toBe(true); + + const event = ContractClassPublishedEvent.fromLog(log); + expect(event.packedPublicBytecode).toEqual(maxBytecode); + }); }); diff --git a/yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.ts b/yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.ts index 73234f5cc6a3..4e1dfecf7244 100644 --- a/yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.ts +++ b/yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.ts @@ -4,7 +4,7 @@ import { FieldReader } from '@aztec/foundation/serialize'; import { bufferFromFields } from '@aztec/stdlib/abi'; import { type ContractClassPublic, - computeContractClassId, + type ContractClassPublicWithCommitment, computePublicBytecodeCommitment, } from '@aztec/stdlib/contract'; import type { ContractClassLog } from '@aztec/stdlib/logs'; @@ -47,34 +47,23 @@ export class ContractClassPublishedEvent { ); } - async toContractClassPublic(): Promise { - const computedClassId = await computeContractClassId({ - artifactHash: this.artifactHash, - privateFunctionsRoot: this.privateFunctionsRoot, - publicBytecodeCommitment: await computePublicBytecodeCommitment(this.packedPublicBytecode), - }); - - if (!computedClassId.equals(this.contractClassId)) { - throw new Error( - `Invalid contract class id: computed ${computedClassId.toString()} but event broadcasted ${this.contractClassId.toString()}`, - ); - } - - if (this.version !== 1) { - throw new Error(`Unexpected contract class version ${this.version}`); - } - + /** Converts the event to a contract class, without computing or validating the bytecode commitment. */ + toContractClassPublic(): ContractClassPublic { return { id: this.contractClassId, artifactHash: this.artifactHash, packedBytecode: this.packedPublicBytecode, privateFunctionsRoot: this.privateFunctionsRoot, - version: this.version, - privateFunctions: [], - utilityFunctions: [], + version: this.version as 1, }; } + /** Converts the event to a contract class with its bytecode commitment (expensive). */ + async toContractClassPublicWithBytecodeCommitment(): Promise { + const publicBytecodeCommitment = await computePublicBytecodeCommitment(this.packedPublicBytecode); + return { ...this.toContractClassPublic(), publicBytecodeCommitment }; + } + public static extractContractClassEvents(logs: ContractClassLog[]): ContractClassPublishedEvent[] { return logs .filter((log: ContractClassLog) => ContractClassPublishedEvent.isContractClassPublishedEvent(log)) diff --git a/yarn-project/protocol-contracts/src/class-registry/index.ts b/yarn-project/protocol-contracts/src/class-registry/index.ts index 7b3db3145893..bf8daaf8ffee 100644 --- a/yarn-project/protocol-contracts/src/class-registry/index.ts +++ b/yarn-project/protocol-contracts/src/class-registry/index.ts @@ -6,8 +6,6 @@ import { makeProtocolContract } from '../make_protocol_contract.js'; import type { ProtocolContract } from '../protocol_contract.js'; export * from './contract_class_published_event.js'; -export * from './private_function_broadcasted_event.js'; -export * from './utility_function_broadcasted_event.js'; export const ContractClassRegistryArtifact = loadContractArtifact(ContractClassRegistryJson as NoirCompiledContract); diff --git a/yarn-project/protocol-contracts/src/class-registry/lazy.ts b/yarn-project/protocol-contracts/src/class-registry/lazy.ts index 634a075d1947..6148d927b79f 100644 --- a/yarn-project/protocol-contracts/src/class-registry/lazy.ts +++ b/yarn-project/protocol-contracts/src/class-registry/lazy.ts @@ -4,8 +4,6 @@ import { makeProtocolContract } from '../make_protocol_contract.js'; import type { ProtocolContract } from '../protocol_contract.js'; export * from './contract_class_published_event.js'; -export * from './private_function_broadcasted_event.js'; -export * from './utility_function_broadcasted_event.js'; let protocolContract: ProtocolContract; let protocolContractArtifact: ContractArtifact; diff --git a/yarn-project/protocol-contracts/src/class-registry/private_function_broadcasted_event.test.ts b/yarn-project/protocol-contracts/src/class-registry/private_function_broadcasted_event.test.ts deleted file mode 100644 index 558b4c056b07..000000000000 --- a/yarn-project/protocol-contracts/src/class-registry/private_function_broadcasted_event.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { setupCustomSnapshotSerializers } from '@aztec/foundation/testing'; -import { ContractClassLog } from '@aztec/stdlib/logs'; - -import { getSamplePrivateFunctionBroadcastedEventPayload } from '../tests/fixtures.js'; -import { PrivateFunctionBroadcastedEvent } from './private_function_broadcasted_event.js'; - -describe('PrivateFunctionBroadcastedEvent', () => { - beforeAll(() => setupCustomSnapshotSerializers(expect)); - - it('parses an event as emitted by the ContractClassRegistry', () => { - const log = ContractClassLog.fromBuffer(getSamplePrivateFunctionBroadcastedEventPayload()); - expect(PrivateFunctionBroadcastedEvent.isPrivateFunctionBroadcastedEvent(log)).toBe(true); - - const event = PrivateFunctionBroadcastedEvent.fromLog(log); - - // See ./__snapshots__/README.md for how to update the snapshot. - expect(event).toMatchSnapshot(); - }); -}); diff --git a/yarn-project/protocol-contracts/src/class-registry/private_function_broadcasted_event.ts b/yarn-project/protocol-contracts/src/class-registry/private_function_broadcasted_event.ts deleted file mode 100644 index 88698959d73c..000000000000 --- a/yarn-project/protocol-contracts/src/class-registry/private_function_broadcasted_event.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { - ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, - CONTRACT_CLASS_REGISTRY_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE, - FUNCTION_TREE_HEIGHT, - MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, -} from '@aztec/constants'; -import { Fr } from '@aztec/foundation/curves/bn254'; -import type { Tuple } from '@aztec/foundation/serialize'; -import { FieldReader } from '@aztec/foundation/serialize'; -import { FunctionSelector, bufferFromFields } from '@aztec/stdlib/abi'; -import type { ExecutablePrivateFunctionWithMembershipProof, PrivateFunction } from '@aztec/stdlib/contract'; -import type { ContractClassLog } from '@aztec/stdlib/logs'; - -import { ProtocolContractAddress } from '../protocol_contract_data.js'; - -/** Event emitted from the ContractClassRegistry. */ -export class PrivateFunctionBroadcastedEvent { - constructor( - public readonly contractClassId: Fr, - public readonly artifactMetadataHash: Fr, - public readonly utilityFunctionsTreeRoot: Fr, - public readonly privateFunctionTreeSiblingPath: Tuple, - public readonly privateFunctionTreeLeafIndex: number, - public readonly artifactFunctionTreeSiblingPath: Tuple, - public readonly artifactFunctionTreeLeafIndex: number, - public readonly privateFunction: BroadcastedPrivateFunction, - ) {} - - static isPrivateFunctionBroadcastedEvent(log: ContractClassLog) { - return ( - log.contractAddress.equals(ProtocolContractAddress.ContractClassRegistry) && - log.fields.fields[0].toBigInt() === CONTRACT_CLASS_REGISTRY_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE - ); - } - - static fromLog(log: ContractClassLog) { - const reader = new FieldReader(log.fields.fields.slice(1)); - const event = PrivateFunctionBroadcastedEvent.fromFields(reader); - while (!reader.isFinished()) { - const field = reader.readField(); - if (!field.isZero()) { - throw new Error(`Unexpected data after parsing PrivateFunctionBroadcastedEvent: ${field.toString()}`); - } - } - - return event; - } - - static fromFields(fields: Fr[] | FieldReader) { - const reader = FieldReader.asReader(fields); - const contractClassId = reader.readField(); - const artifactMetadataHash = reader.readField(); - const utilityFunctionsTreeRoot = reader.readField(); - const privateFunctionTreeSiblingPath = reader.readFieldArray(FUNCTION_TREE_HEIGHT); - const privateFunctionTreeLeafIndex = reader.readField().toNumber(); - const artifactFunctionTreeSiblingPath = reader.readFieldArray(ARTIFACT_FUNCTION_TREE_MAX_HEIGHT); - const artifactFunctionTreeLeafIndex = reader.readField().toNumber(); - const privateFunction = BroadcastedPrivateFunction.fromFields(reader); - - return new PrivateFunctionBroadcastedEvent( - contractClassId, - artifactMetadataHash, - utilityFunctionsTreeRoot, - privateFunctionTreeSiblingPath, - privateFunctionTreeLeafIndex, - artifactFunctionTreeSiblingPath, - artifactFunctionTreeLeafIndex, - privateFunction, - ); - } - - toFunctionWithMembershipProof(): ExecutablePrivateFunctionWithMembershipProof { - return { - ...this.privateFunction, - bytecode: this.privateFunction.bytecode, - functionMetadataHash: this.privateFunction.metadataHash, - artifactMetadataHash: this.artifactMetadataHash, - utilityFunctionsTreeRoot: this.utilityFunctionsTreeRoot, - privateFunctionTreeSiblingPath: this.privateFunctionTreeSiblingPath, - privateFunctionTreeLeafIndex: this.privateFunctionTreeLeafIndex, - artifactTreeSiblingPath: this.artifactFunctionTreeSiblingPath.filter(fr => !fr.isZero()), - artifactTreeLeafIndex: this.artifactFunctionTreeLeafIndex, - }; - } -} - -export class BroadcastedPrivateFunction implements PrivateFunction { - constructor( - /** Selector of the function. Calculated as the hash of the method name and parameters. The specification of this is not enforced by the protocol. */ - public readonly selector: FunctionSelector, - /** Artifact metadata hash */ - public readonly metadataHash: Fr, - /** Hash of the verification key associated to this private function. */ - public readonly vkHash: Fr, - /** ACIR and Brillig bytecode */ - public readonly bytecode: Buffer, - ) {} - - static fromFields(fields: Fr[] | FieldReader) { - const reader = FieldReader.asReader(fields); - const selector = FunctionSelector.fromField(reader.readField()); - const metadataHash = reader.readField(); - const vkHash = reader.readField(); - // The '* 1' removes the 'Type instantiation is excessively deep and possibly infinite. ts(2589)' err - const encodedBytecode = reader.readFieldArray(MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS * 1); - const bytecode = bufferFromFields(encodedBytecode); - return new BroadcastedPrivateFunction(selector, metadataHash, vkHash, bytecode); - } -} diff --git a/yarn-project/protocol-contracts/src/class-registry/utility_function_broadcasted_event.test.ts b/yarn-project/protocol-contracts/src/class-registry/utility_function_broadcasted_event.test.ts deleted file mode 100644 index d87aa2771848..000000000000 --- a/yarn-project/protocol-contracts/src/class-registry/utility_function_broadcasted_event.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { randomBytes } from '@aztec/foundation/crypto/random'; -import { Fr } from '@aztec/foundation/curves/bn254'; -import type { Tuple } from '@aztec/foundation/serialize'; -import { setupCustomSnapshotSerializers } from '@aztec/foundation/testing'; -import { FunctionSelector } from '@aztec/stdlib/abi'; -import { ContractClassLog } from '@aztec/stdlib/logs'; - -import { getSampleUtilityFunctionBroadcastedEventPayload } from '../tests/fixtures.js'; -import { BroadcastedUtilityFunction, UtilityFunctionBroadcastedEvent } from './utility_function_broadcasted_event.js'; - -describe('UtilityFunctionBroadcastedEvent', () => { - beforeAll(() => setupCustomSnapshotSerializers(expect)); - - it('parses an event as emitted by the ContractClassRegistry', () => { - const log = ContractClassLog.fromBuffer(getSampleUtilityFunctionBroadcastedEventPayload()); - expect(UtilityFunctionBroadcastedEvent.isUtilityFunctionBroadcastedEvent(log)).toBe(true); - - const event = UtilityFunctionBroadcastedEvent.fromLog(log); - - // See ./__snapshots__/README.md for how to update the snapshot. - expect(event).toMatchSnapshot(); - }); - - it('filters out zero-elements at the end of the artifact tree sibling path', () => { - const siblingPath: Tuple = [Fr.ZERO, new Fr(1), Fr.ZERO, new Fr(2), Fr.ZERO, new Fr(3), Fr.ZERO]; - const event = new UtilityFunctionBroadcastedEvent( - Fr.random(), - Fr.random(), - Fr.random(), - siblingPath, - 0, - new BroadcastedUtilityFunction(FunctionSelector.random(), Fr.random(), randomBytes(32)), - ); - const filtered = event.toFunctionWithMembershipProof().artifactTreeSiblingPath; - expect(filtered).toEqual([Fr.ZERO, new Fr(1), Fr.ZERO, new Fr(2), Fr.ZERO, new Fr(3)]); - }); -}); diff --git a/yarn-project/protocol-contracts/src/class-registry/utility_function_broadcasted_event.ts b/yarn-project/protocol-contracts/src/class-registry/utility_function_broadcasted_event.ts deleted file mode 100644 index d7cfe8b1bc10..000000000000 --- a/yarn-project/protocol-contracts/src/class-registry/utility_function_broadcasted_event.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { - ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, - CONTRACT_CLASS_REGISTRY_UTILITY_FUNCTION_BROADCASTED_MAGIC_VALUE, - MAX_PACKED_BYTECODE_SIZE_PER_UTILITY_FUNCTION_IN_FIELDS, -} from '@aztec/constants'; -import { removeArrayPaddingEnd } from '@aztec/foundation/collection'; -import { Fr } from '@aztec/foundation/curves/bn254'; -import type { Tuple } from '@aztec/foundation/serialize'; -import { FieldReader } from '@aztec/foundation/serialize'; -import { FunctionSelector, bufferFromFields } from '@aztec/stdlib/abi'; -import type { UtilityFunction, UtilityFunctionWithMembershipProof } from '@aztec/stdlib/contract'; -import type { ContractClassLog } from '@aztec/stdlib/logs'; - -import { ProtocolContractAddress } from '../protocol_contract_data.js'; - -/** Event emitted from the ContractClassRegistry. */ -export class UtilityFunctionBroadcastedEvent { - constructor( - public readonly contractClassId: Fr, - public readonly artifactMetadataHash: Fr, - public readonly privateFunctionsArtifactTreeRoot: Fr, - public readonly artifactFunctionTreeSiblingPath: Tuple, - public readonly artifactFunctionTreeLeafIndex: number, - public readonly utilityFunction: BroadcastedUtilityFunction, - ) {} - - static isUtilityFunctionBroadcastedEvent(log: ContractClassLog) { - return ( - log.contractAddress.equals(ProtocolContractAddress.ContractClassRegistry) && - log.fields.fields[0].toBigInt() === CONTRACT_CLASS_REGISTRY_UTILITY_FUNCTION_BROADCASTED_MAGIC_VALUE - ); - } - - static fromLog(log: ContractClassLog) { - const reader = new FieldReader(log.fields.fields.slice(1)); - const event = UtilityFunctionBroadcastedEvent.fromFields(reader); - while (!reader.isFinished()) { - const field = reader.readField(); - if (!field.isZero()) { - throw new Error(`Unexpected data after parsing UtilityFunctionBroadcastedEvent: ${field.toString()}`); - } - } - - return event; - } - - static fromFields(fields: Fr[] | FieldReader) { - const reader = FieldReader.asReader(fields); - const contractClassId = reader.readField(); - const artifactMetadataHash = reader.readField(); - const privateFunctionsArtifactTreeRoot = reader.readField(); - const artifactFunctionTreeSiblingPath = reader.readFieldArray(ARTIFACT_FUNCTION_TREE_MAX_HEIGHT); - const artifactFunctionTreeLeafIndex = reader.readField().toNumber(); - const utilityFunction = BroadcastedUtilityFunction.fromFields(reader); - - return new UtilityFunctionBroadcastedEvent( - contractClassId, - artifactMetadataHash, - privateFunctionsArtifactTreeRoot, - artifactFunctionTreeSiblingPath, - artifactFunctionTreeLeafIndex, - utilityFunction, - ); - } - - toFunctionWithMembershipProof(): UtilityFunctionWithMembershipProof { - // We should be able to safely remove the zero elements that pad the variable-length sibling path, - // since a sibling with value zero can only occur on the tree leaves, so the sibling path will never end - // in a zero. The only exception is a tree with depth 2 with one non-zero leaf, where the sibling path would - // be a single zero element, but in that case the artifact tree should be just the single leaf. - const artifactTreeSiblingPath = removeArrayPaddingEnd(this.artifactFunctionTreeSiblingPath, Fr.isZero); - return { - ...this.utilityFunction, - bytecode: this.utilityFunction.bytecode, - functionMetadataHash: this.utilityFunction.metadataHash, - artifactMetadataHash: this.artifactMetadataHash, - privateFunctionsArtifactTreeRoot: this.privateFunctionsArtifactTreeRoot, - artifactTreeSiblingPath, - artifactTreeLeafIndex: this.artifactFunctionTreeLeafIndex, - }; - } -} - -export class BroadcastedUtilityFunction implements UtilityFunction { - constructor( - /** Selector of the function. Calculated as the hash of the method name and parameters. The specification of this is not enforced by the protocol. */ - public readonly selector: FunctionSelector, - /** Artifact metadata hash */ - public readonly metadataHash: Fr, - /** Brillig bytecode */ - public readonly bytecode: Buffer, - ) {} - - static fromFields(fields: Fr[] | FieldReader) { - const reader = FieldReader.asReader(fields); - const selector = FunctionSelector.fromField(reader.readField()); - const metadataHash = reader.readField(); - // The '* 1' removes the 'Type instantiation is excessively deep and possibly infinite. ts(2589)' err - const encodedBytecode = reader.readFieldArray(MAX_PACKED_BYTECODE_SIZE_PER_UTILITY_FUNCTION_IN_FIELDS * 1); - const bytecode = bufferFromFields(encodedBytecode); - return new BroadcastedUtilityFunction(selector, metadataHash, bytecode); - } -} diff --git a/yarn-project/protocol-contracts/src/tests/fixtures.ts b/yarn-project/protocol-contracts/src/tests/fixtures.ts index 2d4a1a712057..d71fc48ac162 100644 --- a/yarn-project/protocol-contracts/src/tests/fixtures.ts +++ b/yarn-project/protocol-contracts/src/tests/fixtures.ts @@ -8,18 +8,6 @@ export function getSampleContractClassPublishedEventPayload(): Buffer { return Buffer.from(readFileSync(path).toString(), 'hex'); } -// Generated from end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts with AZTEC_GENERATE_TEST_DATA=1 -export function getSamplePrivateFunctionBroadcastedEventPayload(): Buffer { - const path = getPathToFixture('PrivateFunctionBroadcastedEventData.hex'); - return Buffer.from(readFileSync(path).toString(), 'hex'); -} - -// Generated from end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts with AZTEC_GENERATE_TEST_DATA=1 -export function getSampleUtilityFunctionBroadcastedEventPayload(): Buffer { - const path = getPathToFixture('UtilityFunctionBroadcastedEventData.hex'); - return Buffer.from(readFileSync(path).toString(), 'hex'); -} - // Generated from end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts with AZTEC_GENERATE_TEST_DATA=1 export function getSampleContractInstancePublishedEventPayload(): Buffer { const path = getPathToFixture('ContractInstancePublishedEventData.hex'); diff --git a/yarn-project/prover-client/src/config.ts b/yarn-project/prover-client/src/config.ts index 658558f9eb44..7aef9717fbe6 100644 --- a/yarn-project/prover-client/src/config.ts +++ b/yarn-project/prover-client/src/config.ts @@ -44,14 +44,25 @@ export const bbConfigMappings: ConfigMappingsType = { }, numConcurrentIVCVerifiers: { env: 'BB_NUM_IVC_VERIFIERS', - description: 'Max number of chonk verifiers to run concurrently', + description: 'Max concurrent verifications for the RPC verifier (QueuedIVCVerifier).', ...numberConfigHelper(8), }, bbIVCConcurrency: { env: 'BB_IVC_CONCURRENCY', - description: 'Number of threads to use for IVC verification', + description: 'Thread count for the RPC IVC verifier.', ...numberConfigHelper(1), }, + bbChonkVerifyMaxBatch: { + env: 'BB_CHONK_VERIFY_MAX_BATCH', + description: + 'Upper bound on proofs per batch for the peer chonk batch verifier. Proofs are verified immediately as they arrive; this only caps how many can accumulate while a batch is already being processed.', + ...numberConfigHelper(16), + }, + bbChonkVerifyConcurrency: { + env: 'BB_CHONK_VERIFY_BATCH_CONCURRENCY', + description: 'Thread count for the peer batch verifier parallel reduce. 0 = auto.', + ...numberConfigHelper(6), + }, }; export const proverClientConfigMappings: ConfigMappingsType = { diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index 73c71c03e3e9..1d5d878fb99d 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -104,8 +104,10 @@ export class TestContext { bbBinaryPath: config.expectedBBPath, bbWorkingDirectory: config.bbWorkingDirectory, bbSkipCleanup: config.bbSkipCleanup, - numConcurrentIVCVerifiers: 2, + numConcurrentIVCVerifiers: 8, bbIVCConcurrency: 1, + bbChonkVerifyMaxBatch: 16, + bbChonkVerifyConcurrency: 6, }; localProver = await createProver(bbConfig); } diff --git a/yarn-project/prover-client/src/proving_broker/broker_prover_facade.ts b/yarn-project/prover-client/src/proving_broker/broker_prover_facade.ts index c9537af6c601..8c45fe0f55cd 100644 --- a/yarn-project/prover-client/src/proving_broker/broker_prover_facade.ts +++ b/yarn-project/prover-client/src/proving_broker/broker_prover_facade.ts @@ -4,7 +4,9 @@ import type { NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH, RECURSIVE_PROOF_LENGTH, } from '@aztec/constants'; +import { asyncPool } from '@aztec/foundation/async-pool'; import { EpochNumber } from '@aztec/foundation/branded-types'; +import { chunk } from '@aztec/foundation/collection'; import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log'; import { type PromiseWithResolvers, RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise'; import { truncate } from '@aztec/foundation/string'; @@ -226,17 +228,11 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver { // We collect all returned notifications and return them const allCompleted = new Set(); try { - let numRequests = 0; - while (ids.length > 0) { - const slice = ids.splice(0, SNAPSHOT_SYNC_CHECK_MAX_REQUEST_SIZE); - const completed = await this.broker.getCompletedJobs(slice); + const batches = ids.length > 0 ? chunk(ids, SNAPSHOT_SYNC_CHECK_MAX_REQUEST_SIZE) : [[]]; + await asyncPool(1, batches, async batch => { + const completed = await this.broker.getCompletedJobs(batch); completed.forEach(id => allCompleted.add(id)); - ++numRequests; - } - if (numRequests === 0) { - const final = await this.broker.getCompletedJobs([]); - final.forEach(id => allCompleted.add(id)); - } + }); } catch (err) { this.log.error(`Error thrown when requesting completed job notifications from the broker`, err); } @@ -352,12 +348,8 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver { .map(id => this.jobs.get(id)!) .filter(x => x !== undefined); const totalJobsToRetrieve = toBeRetrieved.length; - let totalJobsRetrieved = 0; - while (toBeRetrieved.length > 0) { - const slice = toBeRetrieved.splice(0, MAX_CONCURRENT_JOB_SETTLED_REQUESTS); - const results = await Promise.all(slice.map(job => processJob(job!))); - totalJobsRetrieved += results.filter(x => x).length; - } + const results = await asyncPool(MAX_CONCURRENT_JOB_SETTLED_REQUESTS, toBeRetrieved, job => processJob(job)); + const totalJobsRetrieved = results.filter(x => x).length; if (totalJobsToRetrieve > 0) { this.log.verbose( `Successfully retrieved ${totalJobsRetrieved} of ${totalJobsToRetrieve} jobs that should be ready, total ready jobs is now: ${this.jobsToRetrieve.size}`, diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index 48465c03d640..6f2ceda0926d 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -119,11 +119,27 @@ export async function createProverNode( { telemetry, logger: log.createChild('l1-tx-utils'), dateProvider }, ); + // Create a funder L1TxUtils from the keystore funding account (if configured) + const fundingSigner = keyStoreManager?.createFundingSigner(); + let funderL1TxUtils: L1TxUtils | undefined; + if (fundingSigner) { + const [funder] = await createL1TxUtilsFromSigners( + publicClient, + [fundingSigner], + { ...config, scope: 'prover' }, + { telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider }, + ); + funderL1TxUtils = funder; + } + const publisherFactory = deps.publisherFactory ?? new ProverPublisherFactory(config, { rollupContract, - publisherManager: new PublisherManager(l1TxUtils, getPublisherConfigFromProverConfig(config), log.getBindings()), + publisherManager: new PublisherManager(l1TxUtils, getPublisherConfigFromProverConfig(config), { + bindings: log.getBindings(), + funder: funderL1TxUtils, + }), telemetry, }); diff --git a/yarn-project/prover-node/src/prover-publisher-factory.ts b/yarn-project/prover-node/src/prover-publisher-factory.ts index 8e1b88d1560e..054d742cf02a 100644 --- a/yarn-project/prover-node/src/prover-publisher-factory.ts +++ b/yarn-project/prover-node/src/prover-publisher-factory.ts @@ -19,11 +19,11 @@ export class ProverPublisherFactory { ) {} public async start() { - await this.deps.publisherManager.loadState(); + await this.deps.publisherManager.start(); } - public stop() { - this.deps.publisherManager.interrupt(); + public async stop() { + await this.deps.publisherManager.stop(); } /** diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index a2d322ff5838..361f7f1008f0 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -20,7 +20,7 @@ import { L1Metrics, type TelemetryClient } from '@aztec/telemetry-client'; import { FullNodeCheckpointsBuilder, NodeKeystoreAdapter, type ValidatorClient } from '@aztec/validator-client'; import { type SequencerClientConfig, getPublisherConfigFromSequencerConfig } from '../config.js'; -import { GlobalVariableBuilder } from '../global_variable_builder/index.js'; +import type { GlobalVariableBuilder } from '../global_variable_builder/index.js'; import { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js'; import { Sequencer, type SequencerConfig } from '../sequencer/index.js'; @@ -65,7 +65,9 @@ export class SequencerClient { dateProvider: DateProvider; epochCache?: EpochCache; l1TxUtils: L1TxUtils[]; + funderL1TxUtils?: L1TxUtils; nodeKeyStore: KeystoreManager; + globalVariableBuilder: GlobalVariableBuilder; }, ) { const { @@ -87,16 +89,14 @@ export class SequencerClient { publicClient, l1TxUtils.map(x => x.getSenderAddress()), ); - const publisherManager = new PublisherManager( - l1TxUtils, - getPublisherConfigFromSequencerConfig(config), - log.getBindings(), - ); + const publisherManager = new PublisherManager(l1TxUtils, getPublisherConfigFromSequencerConfig(config), { + bindings: log.getBindings(), + funder: deps.funderL1TxUtils, + }); const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString()); - const [l1GenesisTime, slotDuration, rollupVersion, rollupManaLimit] = await Promise.all([ + const [l1GenesisTime, slotDuration, rollupManaLimit] = await Promise.all([ rollupContract.getL1GenesisTime(), rollupContract.getSlotDuration(), - rollupContract.getVersion(), rollupContract.getManaLimit().then(Number), ] as const); @@ -140,13 +140,7 @@ export class SequencerClient { const ethereumSlotDuration = config.ethereumSlotDuration; - const globalsBuilder = new GlobalVariableBuilder({ - ...config, - l1GenesisTime, - slotDuration: Number(slotDuration), - ethereumSlotDuration, - rollupVersion, - }); + const globalsBuilder = deps.globalVariableBuilder; // When running in anvil, assume we can post a tx up until one second before the end of an L1 slot. // Otherwise, we need the full L1 slot duration for publishing to ensure inclusion. @@ -202,7 +196,7 @@ export class SequencerClient { await this.validatorClient?.start(); this.sequencer.start(); this.l1Metrics?.start(); - await this.publisherManager.loadState(); + await this.publisherManager.start(); } /** @@ -211,7 +205,7 @@ export class SequencerClient { public async stop() { await this.sequencer.stop(); await this.validatorClient?.stop(); - this.publisherManager.interrupt(); + await this.publisherManager.stop(); this.l1Metrics?.stop(); } diff --git a/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts b/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts index 20c0f236292d..3043f9a56eed 100644 --- a/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts +++ b/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts @@ -1,15 +1,13 @@ -import { createEthereumChain } from '@aztec/ethereum/chain'; -import { makeL1HttpTransport } from '@aztec/ethereum/client'; -import type { L1ContractsConfig } from '@aztec/ethereum/config'; import { RollupContract } from '@aztec/ethereum/contracts'; -import type { L1ReaderConfig } from '@aztec/ethereum/l1-reader'; +import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses'; import type { ViemPublicClient } from '@aztec/ethereum/types'; import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; +import type { DateProvider } from '@aztec/foundation/timer'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { type L1RollupConstants, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers'; +import { type L1RollupConstants, getNextL1SlotTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers'; import { GasFees } from '@aztec/stdlib/gas'; import type { CheckpointGlobalVariables, @@ -17,7 +15,12 @@ import type { } from '@aztec/stdlib/tx'; import { GlobalVariables } from '@aztec/stdlib/tx'; -import { createPublicClient } from 'viem'; +/** Configuration for the GlobalVariableBuilder (excludes L1 client config). */ +export type GlobalVariableBuilderConfig = { + l1Contracts: Pick; + ethereumSlotDuration: number; + rollupVersion: bigint; +} & Pick; /** * Simple global variables builder. @@ -28,7 +31,6 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface { private currentL1BlockNumber: bigint | undefined = undefined; private readonly rollupContract: RollupContract; - private readonly publicClient: ViemPublicClient; private readonly ethereumSlotDuration: number; private readonly aztecSlotDuration: number; private readonly l1GenesisTime: bigint; @@ -37,28 +39,18 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface { private version: Fr; constructor( - config: L1ReaderConfig & - Pick & - Pick & { rollupVersion: bigint }, + private readonly dateProvider: DateProvider, + private readonly publicClient: ViemPublicClient, + config: GlobalVariableBuilderConfig, ) { - const { l1RpcUrls, l1ChainId: chainId, l1Contracts } = config; - - const chain = createEthereumChain(l1RpcUrls, chainId); - this.version = new Fr(config.rollupVersion); - this.chainId = new Fr(chainId); + this.chainId = new Fr(this.publicClient.chain!.id); this.ethereumSlotDuration = config.ethereumSlotDuration; this.aztecSlotDuration = config.slotDuration; this.l1GenesisTime = config.l1GenesisTime; - this.publicClient = createPublicClient({ - chain: chain.chainInfo, - transport: makeL1HttpTransport(chain.rpcUrls, { timeout: config.l1HttpTimeoutMS }), - pollingInterval: config.viemPollingIntervalMS, - }); - - this.rollupContract = new RollupContract(this.publicClient, l1Contracts.rollupAddress); + this.rollupContract = new RollupContract(this.publicClient, config.l1Contracts.rollupAddress); } /** @@ -74,7 +66,10 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface { const earliestTimestamp = await this.rollupContract.getTimestampForSlot( SlotNumber.fromBigInt(BigInt(lastCheckpoint.slotNumber) + 1n), ); - const nextEthTimestamp = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration)); + const nextEthTimestamp = getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), { + l1GenesisTime: this.l1GenesisTime, + ethereumSlotDuration: this.ethereumSlotDuration, + }); const timestamp = earliestTimestamp > nextEthTimestamp ? earliestTimestamp : nextEthTimestamp; return new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true)); @@ -109,7 +104,10 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface { const slot: SlotNumber = maybeSlot ?? (await this.rollupContract.getSlotAt( - BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration)), + getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), { + l1GenesisTime: this.l1GenesisTime, + ethereumSlotDuration: this.ethereumSlotDuration, + }), )); const checkpointGlobalVariables = await this.buildCheckpointGlobalVariables(coinbase, feeRecipient, slot); diff --git a/yarn-project/sequencer-client/src/global_variable_builder/index.ts b/yarn-project/sequencer-client/src/global_variable_builder/index.ts index 5669a0412ae4..a48ed6c244eb 100644 --- a/yarn-project/sequencer-client/src/global_variable_builder/index.ts +++ b/yarn-project/sequencer-client/src/global_variable_builder/index.ts @@ -1 +1 @@ -export { GlobalVariableBuilder } from './global_builder.js'; +export { GlobalVariableBuilder, type GlobalVariableBuilderConfig } from './global_builder.js'; diff --git a/yarn-project/sequencer-client/src/publisher/config.ts b/yarn-project/sequencer-client/src/publisher/config.ts index ba250c31f0d1..a68948536ce8 100644 --- a/yarn-project/sequencer-client/src/publisher/config.ts +++ b/yarn-project/sequencer-client/src/publisher/config.ts @@ -4,6 +4,8 @@ import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from '@aztec/ethereum/l import { type ConfigMappingsType, SecretValue, booleanConfigHelper } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; +import { parseEther } from 'viem'; + /** Configuration of the transaction publisher. */ export type TxSenderConfig = L1ReaderConfig & { /** The private key to be used by the publisher. */ @@ -50,13 +52,37 @@ export type PublisherConfig = L1TxUtilsConfig & publisherForwarderAddress?: EthAddress; /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */ l1TxFailedStore?: string; + /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */ + publisherFundingThreshold?: bigint; + /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */ + publisherFundingAmount?: bigint; }; +/** Shared config mappings for publisher funding, used by both sequencer and prover publisher configs. */ +const publisherFundingConfigMappings = { + publisherFundingThreshold: { + env: 'PUBLISHER_FUNDING_THRESHOLD' as const, + description: + 'Min ETH balance below which a publisher gets funded. Specified in ether (e.g. 0.1). Unset = funding disabled.', + parseEnv: (val: string) => parseEther(val), + }, + publisherFundingAmount: { + env: 'PUBLISHER_FUNDING_AMOUNT' as const, + description: + 'Amount of ETH to send when funding a publisher. Specified in ether (e.g. 0.5). Unset = funding disabled.', + parseEnv: (val: string) => parseEther(val), + }, +}; + export type ProverPublisherConfig = L1TxUtilsConfig & BlobClientConfig & { fishermanMode?: boolean; proverPublisherAllowInvalidStates?: boolean; proverPublisherForwarderAddress?: EthAddress; + /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */ + publisherFundingThreshold?: bigint; + /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */ + publisherFundingAmount?: bigint; }; export type SequencerPublisherConfig = L1TxUtilsConfig & @@ -66,6 +92,10 @@ export type SequencerPublisherConfig = L1TxUtilsConfig & sequencerPublisherForwarderAddress?: EthAddress; /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */ l1TxFailedStore?: string; + /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */ + publisherFundingThreshold?: bigint; + /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */ + publisherFundingAmount?: bigint; }; export function getPublisherConfigFromProverConfig(config: ProverPublisherConfig): PublisherConfig { @@ -142,6 +172,7 @@ export const sequencerPublisherConfigMappings: ConfigMappingsType = { @@ -163,4 +194,5 @@ export const proverPublisherConfigMappings: ConfigMappingsType (val ? EthAddress.fromString(val) : undefined), }, + ...publisherFundingConfigMappings, }; diff --git a/yarn-project/sequencer-client/src/publisher/sequencer-publisher-factory.ts b/yarn-project/sequencer-client/src/publisher/sequencer-publisher-factory.ts index 0583f969d49e..d9ed0a7ccc30 100644 --- a/yarn-project/sequencer-client/src/publisher/sequencer-publisher-factory.ts +++ b/yarn-project/sequencer-client/src/publisher/sequencer-publisher-factory.ts @@ -117,8 +117,8 @@ export class SequencerPublisherFactory { }; } - /** Interrupts all publishers managed by this factory. Used during sequencer shutdown. */ - public interruptAll(): void { - this.deps.publisherManager.interrupt(); + /** Stops all publishers managed by this factory. Used during sequencer shutdown. */ + public async stopAll(): Promise { + await this.deps.publisherManager.stop(); } } diff --git a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts index 75fdbb221715..6571b976bf19 100644 --- a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts @@ -23,6 +23,7 @@ import { TestDateProvider } from '@aztec/foundation/timer'; import { EmpireBaseAbi, RollupAbi } from '@aztec/l1-artifacts'; import { CommitteeAttestationsAndSigners, L2Block, Signature } from '@aztec/stdlib/block'; import { Checkpoint } from '@aztec/stdlib/checkpoint'; +import { EmptyL1RollupConstants } from '@aztec/stdlib/epoch-helpers'; import type { SlashFactoryContract } from '@aztec/stdlib/l1-contracts'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; @@ -138,12 +139,8 @@ describe('SequencerPublisher', () => { slashFactoryContract = mock(); const epochCache = mock(); - epochCache.getEpochAndSlotNow.mockReturnValue({ - epoch: EpochNumber(1), - slot: SlotNumber(2), - ts: 3n, - nowMs: 3000n, - }); + epochCache.getEpochAndSlotNow.mockReturnValue({ epoch: EpochNumber(1), slot: SlotNumber(2), ts: 3n, nowMs: 3000n }); + epochCache.getL1Constants.mockReturnValue(EmptyL1RollupConstants); epochCache.getSlotNow.mockReturnValue(SlotNumber(2)); epochCache.getCommittee.mockResolvedValue({ committee: [], @@ -327,6 +324,7 @@ describe('SequencerPublisher', () => { nowMs: 3000n, }); epochCache.getSlotNow.mockReturnValue(SlotNumber(2)); + epochCache.getL1Constants.mockReturnValue(EmptyL1RollupConstants); epochCache.getCommittee.mockResolvedValue({ committee: [], seed: 1n, diff --git a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts index 971a338d8647..284b83c3870b 100644 --- a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts @@ -42,6 +42,7 @@ import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts'; import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher'; import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block'; import type { Checkpoint } from '@aztec/stdlib/checkpoint'; +import { getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers'; import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts'; import type { CheckpointHeader } from '@aztec/stdlib/rollup'; import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats'; @@ -134,6 +135,7 @@ export class SequencerPublisher { protected log: Logger; protected ethereumSlotDuration: bigint; protected aztecSlotDuration: bigint; + private dateProvider: DateProvider; private blobClient: BlobClientInterface; @@ -187,6 +189,7 @@ export class SequencerPublisher { this.log = deps.log ?? createLogger('sequencer:publisher'); this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration); this.aztecSlotDuration = BigInt(config.aztecSlotDuration); + this.dateProvider = deps.dateProvider; this.epochCache = deps.epochCache; this.lastActions = deps.lastActions; @@ -612,9 +615,10 @@ export class SequencerPublisher { const pipelined = opts.pipelined ?? this.epochCache.isProposerPipeliningEnabled(); const slotOffset = pipelined ? this.aztecSlotDuration : 0n; + const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset; return this.rollupContract - .canProposeAt(tipArchive.toBuffer(), msgSender.toString(), this.ethereumSlotDuration, slotOffset, { + .canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, { forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber, }) .catch(err => { @@ -652,7 +656,7 @@ export class SequencerPublisher { flags, ] as const; - const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration); + const ts = this.getNextL1SlotTimestamp(); const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride( opts?.forcePendingCheckpointNumber, ); @@ -1585,4 +1589,10 @@ export class SequencerPublisher { }, }); } + + /** Returns the timestamp to use when simulating L1 proposal calls */ + private getNextL1SlotTimestamp(): bigint { + const l1Constants = this.epochCache.getL1Constants(); + return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants); + } } diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 1392a1be2ec7..2350fca030b6 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -147,7 +147,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter { this.log.info(`Stopping sequencer`); this.setState(SequencerState.STOPPING, undefined, { force: true }); - this.publisherFactory.interruptAll(); + await this.publisherFactory.stopAll(); await this.runningPromise?.stop(); this.setState(SequencerState.STOPPED, undefined, { force: true }); this.log.info('Stopped sequencer'); @@ -501,8 +501,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter { // Check that the archiver has fully synced the L2 slot before the one we want to propose in. - // TODO(#14766): Archiver reports L1 timestamp based on L1 blocks seen, which means that a missed L1 block will - // cause the archiver L1 timestamp to fall behind, and cause this sequencer to start processing one L1 slot later. + // The archiver reports sync progress via L1 block timestamps and synced checkpoint slots. + // See getSyncedL2SlotNumber for how missed L1 blocks are handled. const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber(); const { slot } = args; if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) { diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index 445949934694..144a39182563 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -55,10 +55,10 @@ export class PublicContractsDB implements PublicContractsDBInterface { this.log = createLogger('simulator:contracts-data-source', bindings); } - public async addContracts(contractDeploymentData: ContractDeploymentData): Promise { + public addContracts(contractDeploymentData: ContractDeploymentData): void { const currentState = this.getCurrentState(); - await this.addContractClassesFromEvents( + this.addContractClassesFromEvents( ContractClassPublishedEvent.extractContractClassEvents(contractDeploymentData.getContractClassLogs()), currentState, ); @@ -69,10 +69,10 @@ export class PublicContractsDB implements PublicContractsDBInterface { ); } - public async addNewContracts(tx: Tx): Promise { + public addNewContracts(tx: Tx): void { const contractDeploymentData = AllContractDeploymentData.fromTx(tx); - await this.addContracts(contractDeploymentData.getNonRevertibleContractDeploymentData()); - await this.addContracts(contractDeploymentData.getRevertibleContractDeploymentData()); + this.addContracts(contractDeploymentData.getNonRevertibleContractDeploymentData()); + this.addContracts(contractDeploymentData.getRevertibleContractDeploymentData()); } /** @@ -174,17 +174,15 @@ export class PublicContractsDB implements PublicContractsDBInterface { return await this.dataSource.getDebugFunctionName(address, selector); } - private async addContractClassesFromEvents( + private addContractClassesFromEvents( contractClassEvents: ContractClassPublishedEvent[], state: ContractsDbCheckpoint, ) { - await Promise.all( - contractClassEvents.map(async (event: ContractClassPublishedEvent) => { - this.log.debug(`Adding class ${event.contractClassId.toString()} to contract state`); - const contractClass = await event.toContractClassPublic(); - state.addClass(event.contractClassId, contractClass); - }), - ); + for (const event of contractClassEvents) { + this.log.debug(`Adding class ${event.contractClassId.toString()} to contract state`); + const contractClass = event.toContractClassPublic(); + state.addClass(event.contractClassId, contractClass); + } } private addContractInstancesFromEvents( diff --git a/yarn-project/simulator/src/public/public_processor/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor/public_processor.test.ts index 23a019bb6080..abc3aedf918e 100644 --- a/yarn-project/simulator/src/public/public_processor/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor/public_processor.test.ts @@ -361,8 +361,8 @@ describe('public_processor', () => { // we want to confirm that even non-revertibles get cleared const contractClassId = await mockContractClassForTx(tx, /*revertible=*/ false); - publicTxSimulator.simulate.mockImplementation(async (simulatedTx: Tx) => { - await contractsDB.addNewContracts(simulatedTx); + publicTxSimulator.simulate.mockImplementation((simulatedTx: Tx) => { + contractsDB.addNewContracts(simulatedTx); throw new Error('Uncaught error'); }); diff --git a/yarn-project/simulator/src/public/public_processor/public_processor.ts b/yarn-project/simulator/src/public/public_processor/public_processor.ts index f8b708d0a568..29a4f6aaadbf 100644 --- a/yarn-project/simulator/src/public/public_processor/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor/public_processor.ts @@ -548,7 +548,7 @@ export class PublicProcessor implements Traceable { // Fee payment insertion has already been done. Do the rest. await this.doTreeInsertionsForPrivateOnlyTx(processedTx); - await this.contractsDB.addNewContracts(tx); + this.contractsDB.addNewContracts(tx); return [processedTx, undefined, []]; } diff --git a/yarn-project/simulator/src/public/public_tx_simulator/contract_provider_for_cpp.ts b/yarn-project/simulator/src/public/public_tx_simulator/contract_provider_for_cpp.ts index 6568a47228e9..9077b8676c1e 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator/contract_provider_for_cpp.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/contract_provider_for_cpp.ts @@ -52,6 +52,7 @@ export class ContractProviderForCpp implements ContractProvider { return serializeWithMessagePack(contractClass); }; + // eslint-disable-next-line require-await public addContracts = async (contractDeploymentDataBuffer: Buffer): Promise => { this.log.trace(`Contract provider callback: addContracts`); @@ -62,7 +63,7 @@ export class ContractProviderForCpp implements ContractProvider { // Add contracts to the contracts DB this.log.trace(`Calling contractsDB.addContracts`); - await this.contractsDB.addContracts(contractDeploymentData); + this.contractsDB.addContracts(contractDeploymentData); }; public getBytecodeCommitment = async (classId: string): Promise => { diff --git a/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts index c07f0d04945c..4e2b95c21c7c 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts @@ -401,7 +401,7 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface { // However, things work as expected because later calls to getters on the hintingContractsDB // will pick up the new contracts and will generate the necessary hints. // So, a consumer of the hints will always see the new contracts. - await this.contractsDB.addContracts(context.nonRevertibleContractDeploymentData); + this.contractsDB.addContracts(context.nonRevertibleContractDeploymentData); } /** @@ -486,7 +486,7 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface { // However, things work as expected because later calls to getters on the hintingContractsDB // will pick up the new contracts and will generate the necessary hints. // So, a consumer of the hints will always see the new contracts. - await this.contractsDB.addContracts(context.revertibleContractDeploymentData); + this.contractsDB.addContracts(context.revertibleContractDeploymentData); } private async payFee(context: PublicTxContext) { diff --git a/yarn-project/stdlib/src/abi/buffer.test.ts b/yarn-project/stdlib/src/abi/buffer.test.ts index 9c3329666c30..d4ca635d5b92 100644 --- a/yarn-project/stdlib/src/abi/buffer.test.ts +++ b/yarn-project/stdlib/src/abi/buffer.test.ts @@ -1,3 +1,5 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; + import { bufferAsFields, bufferFromFields } from './buffer.js'; describe('buffer', () => { @@ -11,4 +13,44 @@ describe('buffer', () => { const buffer = Buffer.from('1234567890abcdef'.repeat(10), 'hex'); expect(() => bufferAsFields(buffer, 3)).toThrow(/exceeds maximum size/); }); + + it('pads with zeros when declared length exceeds payload', () => { + // Create a small buffer, encode it, then truncate the field array before decoding. + const buffer = Buffer.from('aabbccdd', 'hex'); // 4 bytes + const fields = bufferAsFields(buffer, 10); + // Declared length is 4 bytes, stored in fields[0]. Payload fields follow. + // Artificially inflate the declared length to 62 bytes (2 full fields). + + const inflatedFields = [new Fr(62), ...fields.slice(1)]; + const result = bufferFromFields(inflatedFields); + // Result should be exactly 62 bytes: original 4 bytes followed by 58 zero bytes. + expect(result.length).toBe(62); + expect(result.subarray(0, 4).toString('hex')).toEqual('aabbccdd'); + expect(result.subarray(4).every(b => b === 0)).toBe(true); + }); + + it('pads with zeros when payload fields are truncated', () => { + // Simulate the blob reconstruction scenario: declared length says 93 bytes (3 fields), + // but only 1 payload field is present. + + const payloadField = Fr.fromBuffer( + Buffer.from('00' + 'ab'.repeat(31), 'hex'), // 31 bytes of 0xab + ); + // Declared length = 93 bytes (would need 3 fields), but only 1 field in payload. + const fields = [new Fr(93), payloadField]; + const result = bufferFromFields(fields); + expect(result.length).toBe(93); + // First 31 bytes come from the single payload field. + expect(result.subarray(0, 31).every(b => b === 0xab)).toBe(true); + // Remaining 62 bytes are zero-padded. + expect(result.subarray(31).every(b => b === 0)).toBe(true); + }); + + it('returns exact buffer when payload matches declared length', () => { + const buffer = Buffer.from('ff'.repeat(31), 'hex'); // exactly 1 field of payload + const fields = bufferAsFields(buffer, 5); + const result = bufferFromFields(fields); + expect(result.length).toBe(31); + expect(result.toString('hex')).toEqual(buffer.toString('hex')); + }); }); diff --git a/yarn-project/stdlib/src/abi/buffer.ts b/yarn-project/stdlib/src/abi/buffer.ts index 075143244254..42d4ea8eba8a 100644 --- a/yarn-project/stdlib/src/abi/buffer.ts +++ b/yarn-project/stdlib/src/abi/buffer.ts @@ -26,11 +26,32 @@ export function bufferAsFields(input: Buffer, targetLength: number): Fr[] { } /** - * Recovers a buffer from an array of fields. - * @param fields - An output from bufferAsFields. - * @returns The recovered buffer. + * Recovers a buffer from an array of fields previously encoded with bufferAsFields. + * + * The first field encodes the byte length of the original buffer. The remaining fields + * each carry 31 bytes of payload (the leading byte of each 32-byte field element is skipped). + * + * If the declared byte length exceeds the bytes available from the payload fields, the result + * is zero-padded to the full declared length. This is important for correctness when the field + * array has been truncated (e.g. contract class logs reconstructed from blobs using a short + * emittedLength): without padding, the resulting buffer would be shorter than declared, causing + * bytecode commitment computations to diverge from what the circuit produced. + * + * @param fields - An output from bufferAsFields: [byteLength, ...payloadFields]. + * @returns A buffer of exactly `byteLength` bytes. */ export function bufferFromFields(fields: Fr[]): Buffer { const [length, ...payload] = fields; - return Buffer.concat(payload.map(f => f.toBuffer().subarray(1))).subarray(0, length.toNumber()); + const byteLength = length.toNumber(); + const raw = Buffer.concat(payload.map(f => f.toBuffer().subarray(1))); + if (raw.length >= byteLength) { + return raw.subarray(0, byteLength); + } + // Pad with zeros if the declared length exceeds the available payload bytes. + // This ensures the returned buffer always matches the declared length, so that + // downstream bytecode commitment computations are consistent even when the + // source field array was truncated (e.g. reconstructed from blob with a short emittedLength). + const result = Buffer.alloc(byteLength); + raw.copy(result); + return result; } diff --git a/yarn-project/stdlib/src/block/l2_block_source.ts b/yarn-project/stdlib/src/block/l2_block_source.ts index 8b81bdd340b2..77dfd2eb1d93 100644 --- a/yarn-project/stdlib/src/block/l2_block_source.ts +++ b/yarn-project/stdlib/src/block/l2_block_source.ts @@ -175,8 +175,10 @@ export interface L2BlockSource { getSettledTxReceipt(txHash: TxHash): Promise; /** - * Returns the last L2 slot number that has been fully synchronized from L1. - * An L2 slot is fully synced when all L1 blocks that fall within its time range have been processed. + * Returns the last L2 slot number for which we have all L1 data needed to build the next checkpoint. + * Determined by the max of two signals: L1 block sync progress and latest synced checkpoint slot. + * The checkpoint signal handles missed L1 blocks, since a published checkpoint seals the message tree + * for the next checkpoint via the inbox LAG mechanism. */ getSyncedL2SlotNumber(): Promise; diff --git a/yarn-project/stdlib/src/contract/contract_address.test.ts b/yarn-project/stdlib/src/contract/contract_address.test.ts index 2b93d5a47520..d882357eccd3 100644 --- a/yarn-project/stdlib/src/contract/contract_address.test.ts +++ b/yarn-project/stdlib/src/contract/contract_address.test.ts @@ -1,4 +1,6 @@ import { Fr } from '@aztec/foundation/curves/bn254'; +import { createLogger } from '@aztec/foundation/log'; +import { elapsed } from '@aztec/foundation/timer'; import { type FunctionAbi, FunctionType } from '../abi/index.js'; import { AztecAddress } from '../aztec-address/index.js'; @@ -64,17 +66,21 @@ describe('ContractAddress', () => { const initializationHash = new Fr(5n); const deployer = AztecAddress.fromField(new Fr(7)); const publicKeys = (await deriveKeys(secretKey)).publicKeys; - const address = ( - await computeContractAddressFromInstance({ - publicKeys, - salt, - originalContractClassId: contractClassId, - currentContractClassId: contractClassId, - initializationHash, - deployer, - version: 1, - }) - ).toString(); - expect(address).toMatchInlineSnapshot(`"0x2cea4bfccb4a185354cbbd9eb5a39ace117abf1f9381c5b6167b1a6f94a0672c"`); + const instance = { + publicKeys, + salt, + originalContractClassId: contractClassId, + currentContractClassId: contractClassId, + initializationHash, + deployer, + version: 1 as const, + }; + + const [ms, address] = await elapsed(computeContractAddressFromInstance(instance)); + const logger = createLogger('stdlib:contract_address:test'); + logger.info(`Computed contract address from instance in ${ms}ms`); + expect(address.toString()).toMatchInlineSnapshot( + `"0x2cea4bfccb4a185354cbbd9eb5a39ace117abf1f9381c5b6167b1a6f94a0672c"`, + ); }); }); diff --git a/yarn-project/stdlib/src/contract/contract_class_id.test.ts b/yarn-project/stdlib/src/contract/contract_class_id.test.ts index 224957aa6431..04df3321ab02 100644 --- a/yarn-project/stdlib/src/contract/contract_class_id.test.ts +++ b/yarn-project/stdlib/src/contract/contract_class_id.test.ts @@ -1,6 +1,10 @@ import { Fr } from '@aztec/foundation/curves/bn254'; +import { createLogger } from '@aztec/foundation/log'; +import { elapsed } from '@aztec/foundation/timer'; import { FunctionSelector } from '../abi/function_selector.js'; +import { getBenchmarkContractArtifact, getTestContractArtifact, getTokenContractArtifact } from '../tests/fixtures.js'; +import { getContractClassFromArtifact } from './contract_class.js'; import { computeContractClassId } from './contract_class_id.js'; import type { ContractClass } from './interfaces/contract_class.js'; @@ -18,5 +22,20 @@ describe('ContractClass', () => { `"0x2926577ccab09f8e4600550792066ed9d6ce530a973ac2b81a36eaebee56ad44"`, ); }); + + it('calculates the contract class id for a real contract artifact', async () => { + const artifacts = [getBenchmarkContractArtifact(), getTokenContractArtifact(), getTestContractArtifact()]; + const logger = createLogger('stdlib:contract_class_id:test'); + + for (const artifact of artifacts) { + const contractClass = await getContractClassFromArtifact(artifact); + + const [ms, contractClassId] = await elapsed(computeContractClassId(contractClass)); + logger.info(`Computed contract class id ${contractClassId} in ${ms}ms`); + + expect(contractClassId.toString()).toHaveLength(66); // 0x + 64 hex chars + expect(contractClassId.toBigInt()).toBeGreaterThan(0n); + } + }); }); }); diff --git a/yarn-project/stdlib/src/contract/index.ts b/yarn-project/stdlib/src/contract/index.ts index 92f1d7bb517c..d038c43fd6cf 100644 --- a/yarn-project/stdlib/src/contract/index.ts +++ b/yarn-project/stdlib/src/contract/index.ts @@ -6,8 +6,6 @@ export * from './contract_deployment_data.js'; export * from './contract_instance.js'; export * from './contract_instance_update.js'; export * from './private_function.js'; -export * from './private_function_membership_proof.js'; -export * from './utility_function_membership_proof.js'; export * from './interfaces/index.js'; export * from './contract_function_dao.js'; export * from './partial_address.js'; diff --git a/yarn-project/stdlib/src/contract/interfaces/contract_class.ts b/yarn-project/stdlib/src/contract/interfaces/contract_class.ts index e7525fea4a85..33aca27d749f 100644 --- a/yarn-project/stdlib/src/contract/interfaces/contract_class.ts +++ b/yarn-project/stdlib/src/contract/interfaces/contract_class.ts @@ -42,79 +42,6 @@ const PrivateFunctionSchema = zodFor()( }), ); -/** Private function definition with executable bytecode. */ -export interface ExecutablePrivateFunction extends PrivateFunction { - /** ACIR and Brillig bytecode */ - bytecode: Buffer; -} - -const ExecutablePrivateFunctionSchema = zodFor()( - PrivateFunctionSchema.and(z.object({ bytecode: schemas.Buffer })), -); - -/** Utility function definition. */ -export interface UtilityFunction { - /** Selector of the function. Calculated as the hash of the method name and parameters. The specification of this is not enforced by the protocol. */ - selector: FunctionSelector; - /** Brillig. */ - bytecode: Buffer; -} - -const UtilityFunctionSchema = zodFor()( - z.object({ - selector: FunctionSelector.schema, - bytecode: schemas.Buffer, - }), -); - -/** Sibling paths and sibling commitments for proving membership of a private function within a contract class. */ -export type PrivateFunctionMembershipProof = { - artifactMetadataHash: Fr; - functionMetadataHash: Fr; - utilityFunctionsTreeRoot: Fr; - privateFunctionTreeSiblingPath: Fr[]; - privateFunctionTreeLeafIndex: number; - artifactTreeSiblingPath: Fr[]; - artifactTreeLeafIndex: number; -}; - -const PrivateFunctionMembershipProofSchema = zodFor()( - z.object({ - artifactMetadataHash: schemas.Fr, - functionMetadataHash: schemas.Fr, - utilityFunctionsTreeRoot: schemas.Fr, - privateFunctionTreeSiblingPath: z.array(schemas.Fr), - privateFunctionTreeLeafIndex: schemas.Integer, - artifactTreeSiblingPath: z.array(schemas.Fr), - artifactTreeLeafIndex: schemas.Integer, - }), -); - -/** A private function with a membership proof. */ -export type ExecutablePrivateFunctionWithMembershipProof = ExecutablePrivateFunction & PrivateFunctionMembershipProof; - -/** Sibling paths and commitments for proving membership of a utility function within a contract class. */ -export type UtilityFunctionMembershipProof = { - artifactMetadataHash: Fr; - functionMetadataHash: Fr; - privateFunctionsArtifactTreeRoot: Fr; - artifactTreeSiblingPath: Fr[]; - artifactTreeLeafIndex: number; -}; - -const UtilityFunctionMembershipProofSchema = zodFor()( - z.object({ - artifactMetadataHash: schemas.Fr, - functionMetadataHash: schemas.Fr, - privateFunctionsArtifactTreeRoot: schemas.Fr, - artifactTreeSiblingPath: z.array(schemas.Fr), - artifactTreeLeafIndex: schemas.Integer, - }), -); - -/** A utility function with a membership proof. */ -export type UtilityFunctionWithMembershipProof = UtilityFunction & UtilityFunctionMembershipProof; - export const ContractClassSchema = zodFor()( z.object({ version: z.literal(VERSION), @@ -143,11 +70,8 @@ export const ContractClassWithIdSchema = zodFor()( }), ); -/** A contract class with public bytecode information, and optional private and utility functions. */ -export type ContractClassPublic = { - privateFunctions: ExecutablePrivateFunctionWithMembershipProof[]; - utilityFunctions: UtilityFunctionWithMembershipProof[]; -} & Pick & +/** A contract class with public bytecode information. */ +export type ContractClassPublic = Pick & Omit; export type ContractClassPublicWithCommitment = ContractClassPublic & @@ -158,8 +82,6 @@ export const ContractClassPublicSchema = zodFor()( .object({ id: schemas.Fr, privateFunctionsRoot: schemas.Fr, - privateFunctions: z.array(ExecutablePrivateFunctionSchema.and(PrivateFunctionMembershipProofSchema)), - utilityFunctions: z.array(UtilityFunctionSchema.and(UtilityFunctionMembershipProofSchema)), }) .and(ContractClassSchema.omit({ privateFunctions: true })), ); @@ -179,8 +101,6 @@ export function contractClassPublicFromPlainObject(obj: any): ContractClassPubli version: 1, artifactHash: Fr.fromPlainObject(obj.artifactHash), privateFunctionsRoot: Fr.fromPlainObject(obj.privateFunctionsRoot), - privateFunctions: [], - utilityFunctions: [], packedBytecode: obj.packedBytecode instanceof Buffer ? obj.packedBytecode : Buffer.from(obj.packedBytecode), }; } diff --git a/yarn-project/stdlib/src/contract/private_function_membership_proof.test.ts b/yarn-project/stdlib/src/contract/private_function_membership_proof.test.ts deleted file mode 100644 index 611069275b98..000000000000 --- a/yarn-project/stdlib/src/contract/private_function_membership_proof.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Fr } from '@aztec/foundation/curves/bn254'; - -import { type ContractArtifact, type FunctionArtifact, FunctionSelector, FunctionType } from '../abi/index.js'; -import { getBenchmarkContractArtifact } from '../tests/fixtures.js'; -import { computeVerificationKeyHash, getContractClassFromArtifact } from './contract_class.js'; -import type { ContractClassIdPreimage } from './contract_class_id.js'; -import type { ContractClass } from './interfaces/contract_class.js'; -import { - createPrivateFunctionMembershipProof, - isValidPrivateFunctionMembershipProof, -} from './private_function_membership_proof.js'; - -describe('private_function_membership_proof', () => { - let artifact: ContractArtifact; - let contractClass: ContractClass & ContractClassIdPreimage; - let privateFunction: FunctionArtifact; - let vkHash: Fr; - let selector: FunctionSelector; - - beforeAll(async () => { - artifact = getBenchmarkContractArtifact(); - contractClass = await getContractClassFromArtifact(artifact); - privateFunction = artifact.functions.findLast(fn => fn.functionType === FunctionType.PRIVATE)!; - vkHash = await computeVerificationKeyHash(privateFunction); - selector = await FunctionSelector.fromNameAndParameters(privateFunction); - }); - - it('computes and verifies a proof', async () => { - const proof = await createPrivateFunctionMembershipProof(selector, artifact); - const fn = { ...privateFunction, ...proof, selector, vkHash }; - await expect(isValidPrivateFunctionMembershipProof(fn, contractClass)).resolves.toBeTruthy(); - }); - - test.each([ - 'artifactTreeSiblingPath', - 'artifactMetadataHash', - 'functionMetadataHash', - 'utilityFunctionsTreeRoot', - 'privateFunctionTreeSiblingPath', - ] as const)('fails proof if %s is mangled', async field => { - const proof = await createPrivateFunctionMembershipProof(selector, artifact); - const original = proof[field]; - const mangled = Array.isArray(original) ? [Fr.random(), ...original.slice(1)] : Fr.random(); - const wrong = { ...proof, [field]: mangled }; - const fn = { ...privateFunction, ...wrong, selector, vkHash }; - await expect(isValidPrivateFunctionMembershipProof(fn, contractClass)).resolves.toBeFalsy(); - }); -}); diff --git a/yarn-project/stdlib/src/contract/private_function_membership_proof.ts b/yarn-project/stdlib/src/contract/private_function_membership_proof.ts deleted file mode 100644 index c2689d3efb04..000000000000 --- a/yarn-project/stdlib/src/contract/private_function_membership_proof.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { poseidon2Hash } from '@aztec/foundation/crypto/poseidon'; -import { Fr } from '@aztec/foundation/curves/bn254'; -import { createLogger } from '@aztec/foundation/log'; -import { computeRootFromSiblingPath } from '@aztec/foundation/trees'; - -import { type ContractArtifact, FunctionSelector, FunctionType } from '../abi/index.js'; -import { - computeArtifactFunctionTree, - computeArtifactHash, - computeArtifactHashPreimage, - computeFunctionArtifactHash, - computeFunctionMetadataHash, - getArtifactMerkleTreeHasher, -} from './artifact_hash.js'; -import { getContractClassPrivateFunctionFromArtifact } from './contract_class.js'; -import type { - ContractClassPublic, - ExecutablePrivateFunctionWithMembershipProof, - PrivateFunctionMembershipProof, -} from './interfaces/index.js'; -import { computePrivateFunctionLeaf, computePrivateFunctionsTree } from './private_function.js'; - -/** - * Creates a membership proof for a private function in a contract class, to be verified via `isValidPrivateFunctionMembershipProof`. - * @param selector - Selector of the function to create the proof for. - * @param artifact - Artifact of the contract class where the function is defined. - */ -export async function createPrivateFunctionMembershipProof( - selector: FunctionSelector, - artifact: ContractArtifact, -): Promise { - const log = createLogger('circuits:function_membership_proof'); - - // Locate private function definition and artifact - const privateFunctions = artifact.functions.filter(fn => fn.functionType === FunctionType.PRIVATE); - const privateFunctionsFromArtifact = await Promise.all( - privateFunctions.map(getContractClassPrivateFunctionFromArtifact), - ); - const privateFunction = privateFunctionsFromArtifact.find(fn => fn.selector.equals(selector)); - const privateFunctionsAndSelectors = await Promise.all( - privateFunctions.map(async fn => ({ - fn, - selector: await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters), - })), - ); - const privateFunctionArtifact = privateFunctionsAndSelectors.find(fnAndSelector => - selector.equals(fnAndSelector.selector), - )?.fn; - if (!privateFunction || !privateFunctionArtifact) { - throw new Error(`Private function with selector ${selector.toString()} not found`); - } - - // Compute preimage for the artifact hash - const { utilityFunctionRoot: utilityFunctionsTreeRoot, metadataHash: artifactMetadataHash } = - await computeArtifactHashPreimage(artifact); - - // We need two sibling paths because private function information is split across two trees: - // The "private function tree" captures the selectors and verification keys, and is used in the kernel circuit for verifying the proof generated by the app circuit. - const functionLeaf = await computePrivateFunctionLeaf(privateFunction); - const functionsTree = await computePrivateFunctionsTree(privateFunctionsFromArtifact); - const functionsTreeLeafIndex = functionsTree.getIndex(functionLeaf); - const functionsTreeSiblingPath = functionsTree.getSiblingPath(functionsTreeLeafIndex).map(Fr.fromBuffer); - - // And the "artifact tree" captures function bytecode and metadata, and is used by the pxe to check that its executing the code it's supposed to be executing, but it never goes into circuits. - const functionMetadataHash = computeFunctionMetadataHash(privateFunctionArtifact); - const functionArtifactHash = await computeFunctionArtifactHash({ ...privateFunctionArtifact, functionMetadataHash }); - const artifactTree = (await computeArtifactFunctionTree(artifact, FunctionType.PRIVATE))!; - const artifactTreeLeafIndex = artifactTree.getIndex(functionArtifactHash.toBuffer()); - const artifactTreeSiblingPath = artifactTree.getSiblingPath(artifactTreeLeafIndex).map(Fr.fromBuffer); - - log.debug(`Computed proof for private function with selector ${selector.toString()}`, { - functionArtifactHash, - functionMetadataHash, - functionLeaf: '0x' + functionLeaf.toString('hex'), - artifactMetadataHash, - privateFunctionsTreeRoot: '0x' + functionsTree.root.toString('hex'), - utilityFunctionsTreeRoot, - artifactFunctionTreeSiblingPath: artifactTreeSiblingPath.map(fr => fr.toString()).join(','), - privateFunctionTreeSiblingPath: functionsTreeSiblingPath.map(fr => fr.toString()).join(','), - }); - - return { - artifactTreeSiblingPath, - artifactTreeLeafIndex, - artifactMetadataHash, - functionMetadataHash, - utilityFunctionsTreeRoot, - privateFunctionTreeSiblingPath: functionsTreeSiblingPath, - privateFunctionTreeLeafIndex: functionsTreeLeafIndex, - }; -} - -/** - * Verifies that a private function with a membership proof as emitted by the ClassRegistry contract is valid, - * as defined in the protocol specs at contract-deployment/classes: - * - * ``` - * // Load contract class from local db - * contract_class = db.get_contract_class(contract_class_id) - * - * // Compute function leaf and assert it belongs to the private functions tree - * function_leaf = pedersen([selector as Field, vk_hash], GENERATOR__PRIVATE_FUNCTION_LEAF) - * computed_private_function_tree_root = compute_root(function_leaf, private_function_tree_sibling_path) - * assert computed_private_function_tree_root == contract_class.private_functions_root - * - * // Compute artifact leaf and assert it belongs to the artifact - * artifact_function_leaf = sha256(selector, metadata_hash, sha256(bytecode)) - * computed_artifact_private_function_tree_root = compute_root(artifact_function_leaf, artifact_function_tree_sibling_path) - * computed_artifact_hash = sha256(computed_artifact_private_function_tree_root, utility_functions_artifact_tree_root, artifact_metadata_hash) - * assert computed_artifact_hash == contract_class.artifact_hash - * ``` - * @param fn - Function to check membership proof for. - * @param contractClass - In which contract class the function is expected to be. - */ -export async function isValidPrivateFunctionMembershipProof( - fn: ExecutablePrivateFunctionWithMembershipProof, - contractClass: Pick, -) { - const log = createLogger('circuits:function_membership_proof'); - - // Check private function tree membership - const functionLeaf = await computePrivateFunctionLeaf(fn); - const rootBuffer = await computeRootFromSiblingPath( - functionLeaf, - fn.privateFunctionTreeSiblingPath.map(fr => fr.toBuffer()), - fn.privateFunctionTreeLeafIndex, - async (left, right) => (await poseidon2Hash([left, right])).toBuffer(), - ); - const computedPrivateFunctionTreeRoot = Fr.fromBuffer(rootBuffer); - if (!contractClass.privateFunctionsRoot.equals(computedPrivateFunctionTreeRoot)) { - log.debug(`Private function tree root mismatch`, { - expectedPrivateFunctionTreeRoot: contractClass.privateFunctionsRoot, - computedPrivateFunctionTreeRoot: computedPrivateFunctionTreeRoot, - computedFunctionLeaf: '0x' + functionLeaf.toString('hex'), - }); - return false; - } - - // Check artifact hash - const functionArtifactHash = await computeFunctionArtifactHash(fn); - const computedArtifactPrivateFunctionTreeRootBuffer = await computeRootFromSiblingPath( - functionArtifactHash.toBuffer(), - fn.artifactTreeSiblingPath.map(fr => fr.toBuffer()), - fn.artifactTreeLeafIndex, - getArtifactMerkleTreeHasher(), - ); - const computedArtifactPrivateFunctionTreeRoot = Fr.fromBuffer(computedArtifactPrivateFunctionTreeRootBuffer); - const computedArtifactHash = await computeArtifactHash({ - privateFunctionRoot: computedArtifactPrivateFunctionTreeRoot, - utilityFunctionRoot: fn.utilityFunctionsTreeRoot, - metadataHash: fn.artifactMetadataHash, - }); - if (!contractClass.artifactHash.equals(computedArtifactHash)) { - log.debug(`Artifact hash mismatch`, { - expected: contractClass.artifactHash, - computedArtifactHash, - computedFunctionArtifactHash: functionArtifactHash, - computedArtifactPrivateFunctionTreeRoot, - utilityFunctionRoot: fn.utilityFunctionsTreeRoot, - metadataHash: fn.artifactMetadataHash, - artifactFunctionTreeSiblingPath: fn.artifactTreeSiblingPath.map(fr => fr.toString()).join(','), - }); - return false; - } - - return true; -} diff --git a/yarn-project/stdlib/src/contract/utility_function_membership_proof.test.ts b/yarn-project/stdlib/src/contract/utility_function_membership_proof.test.ts deleted file mode 100644 index 1c76520c2f54..000000000000 --- a/yarn-project/stdlib/src/contract/utility_function_membership_proof.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { BlockNumber } from '@aztec/foundation/branded-types'; -import { Fr } from '@aztec/foundation/curves/bn254'; - -import { type ContractArtifact, type FunctionArtifact, FunctionSelector, FunctionType } from '../abi/index.js'; -import { getTestContractArtifact } from '../tests/fixtures.js'; -import { getContractClassFromArtifact } from './contract_class.js'; -import type { ContractClassIdPreimage } from './contract_class_id.js'; -import type { ContractClass } from './interfaces/contract_class.js'; -import { - createUtilityFunctionMembershipProof, - isValidUtilityFunctionMembershipProof, -} from './utility_function_membership_proof.js'; - -describe('utility_function_membership_proof', () => { - let artifact: ContractArtifact; - let contractClass: ContractClass & ContractClassIdPreimage; - let utilityFunction: FunctionArtifact; - const vkHash: Fr = Fr.random(); - let selector: FunctionSelector; - - beforeEach(async () => { - artifact = getTestContractArtifact(); - contractClass = await getContractClassFromArtifact(artifact); - utilityFunction = artifact.functions.findLast(fn => fn.functionType === FunctionType.UTILITY)!; - selector = await FunctionSelector.fromNameAndParameters(utilityFunction); - }); - - const isUtility = (fn: { functionType: FunctionType }) => fn.functionType === FunctionType.UTILITY; - - it('computes and verifies a proof', async () => { - expect(utilityFunction).toBeDefined(); - const proof = await createUtilityFunctionMembershipProof(selector, artifact); - const fn = { ...utilityFunction, ...proof, selector }; - await expect(isValidUtilityFunctionMembershipProof(fn, contractClass)).resolves.toBeTruthy(); - }); - - it('handles a contract with a single function', async () => { - // Remove all utility functions from the contract but one - const utilityFns = artifact.functions.filter(isUtility); - artifact.functions = artifact.functions.filter(fn => !isUtility(fn) || fn === utilityFns[0]); - expect(artifact.functions.filter(isUtility).length).toBe(BlockNumber(1)); - - const utilityFunction = utilityFns[0]; - const selector = await FunctionSelector.fromNameAndParameters(utilityFunction); - - const proof = await createUtilityFunctionMembershipProof(selector, artifact); - expect(proof.artifactTreeSiblingPath.length).toBe(0); - - const fn = { ...utilityFunction, ...proof, selector }; - const contractClass = await getContractClassFromArtifact(artifact); - await expect(isValidUtilityFunctionMembershipProof(fn, contractClass)).resolves.toBeTruthy(); - }); - - test.each(['artifactTreeSiblingPath', 'artifactMetadataHash', 'functionMetadataHash'] as const)( - 'fails proof if %s is mangled', - async field => { - const proof = await createUtilityFunctionMembershipProof(selector, artifact); - const original = proof[field]; - const mangled = Array.isArray(original) ? [Fr.random(), ...original.slice(1)] : Fr.random(); - const wrong = { ...proof, [field]: mangled }; - const fn = { ...utilityFunction, ...wrong, selector, vkHash }; - await expect(isValidUtilityFunctionMembershipProof(fn, contractClass)).resolves.toBeFalsy(); - }, - ); -}); diff --git a/yarn-project/stdlib/src/contract/utility_function_membership_proof.ts b/yarn-project/stdlib/src/contract/utility_function_membership_proof.ts deleted file mode 100644 index 78a65b0d3472..000000000000 --- a/yarn-project/stdlib/src/contract/utility_function_membership_proof.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { Fr } from '@aztec/foundation/curves/bn254'; -import { createLogger } from '@aztec/foundation/log'; -import { computeRootFromSiblingPath } from '@aztec/foundation/trees'; - -import { type ContractArtifact, FunctionSelector, FunctionType } from '../abi/index.js'; -import { - computeArtifactFunctionTree, - computeArtifactHash, - computeArtifactHashPreimage, - computeFunctionArtifactHash, - computeFunctionMetadataHash, - getArtifactMerkleTreeHasher, -} from './artifact_hash.js'; -import type { - ContractClassPublic, - UtilityFunctionMembershipProof, - UtilityFunctionWithMembershipProof, -} from './interfaces/index.js'; - -/** - * Creates a membership proof for a utility function in a contract class, to be verified via `isValidUtilityFunctionMembershipProof`. - * @param selector - Selector of the function to create the proof for. - * @param artifact - Artifact of the contract class where the function is defined. - */ -export async function createUtilityFunctionMembershipProof( - selector: FunctionSelector, - artifact: ContractArtifact, -): Promise { - const log = createLogger('circuits:function_membership_proof'); - - // Locate function artifact - const utilityFunctions = artifact.functions.filter(fn => fn.functionType === FunctionType.UTILITY); - const utilityFunctionsAndSelectors = await Promise.all( - utilityFunctions.map(async fn => ({ fn, selector: await FunctionSelector.fromNameAndParameters(fn) })), - ); - const fn = utilityFunctionsAndSelectors.find(fnAndSelector => selector.equals(fnAndSelector.selector))?.fn; - if (!fn) { - throw new Error(`Utility function with selector ${selector.toString()} not found`); - } - // Compute preimage for the artifact hash - const { privateFunctionRoot: privateFunctionsArtifactTreeRoot, metadataHash: artifactMetadataHash } = - await computeArtifactHashPreimage(artifact); - - // Compute the sibling path for the "artifact tree" - const functionMetadataHash = computeFunctionMetadataHash(fn); - const functionArtifactHash = await computeFunctionArtifactHash({ ...fn, functionMetadataHash }); - const artifactTree = (await computeArtifactFunctionTree(artifact, FunctionType.UTILITY))!; - const artifactTreeLeafIndex = artifactTree.getIndex(functionArtifactHash.toBuffer()); - const artifactTreeSiblingPath = artifactTree.getSiblingPath(artifactTreeLeafIndex).map(Fr.fromBuffer); - - log.debug(`Computed proof for utility function with selector ${selector.toString()}`, { - functionArtifactHash, - functionMetadataHash, - artifactMetadataHash, - artifactFunctionTreeSiblingPath: artifactTreeSiblingPath.map(fr => fr.toString()).join(','), - privateFunctionsArtifactTreeRoot, - }); - - return { - artifactTreeSiblingPath, - artifactTreeLeafIndex, - artifactMetadataHash, - functionMetadataHash, - privateFunctionsArtifactTreeRoot, - }; -} - -/** - * Verifies that a utility function with a membership proof as emitted by the ClassRegistry contract is valid, - * as defined in the protocol specs at contract-deployment/classes: - * - * ``` - * // Load contract class from local db - * contract_class = db.get_contract_class(contract_class_id) - * - * // Compute artifact leaf and assert it belongs to the artifact - * artifact_function_leaf = sha256(selector, metadata_hash, sha256(bytecode)) - * computed_artifact_utility_function_tree_root = compute_root(artifact_function_leaf, artifact_function_tree_sibling_path, artifact_function_tree_leaf_index) - * computed_artifact_hash = sha256(private_functions_artifact_tree_root, computed_artifact_utility_function_tree_root, artifact_metadata_hash) - * assert computed_artifact_hash == contract_class.artifact_hash - * ``` - * @param fn - Function to check membership proof for. - * @param contractClass - In which contract class the function is expected to be. - */ -export async function isValidUtilityFunctionMembershipProof( - fn: UtilityFunctionWithMembershipProof, - contractClass: Pick, -) { - const log = createLogger('circuits:function_membership_proof'); - - const functionArtifactHash = await computeFunctionArtifactHash(fn); - const computedArtifactFunctionTreeRootBuffer = await computeRootFromSiblingPath( - functionArtifactHash.toBuffer(), - fn.artifactTreeSiblingPath.map(fr => fr.toBuffer()), - fn.artifactTreeLeafIndex, - getArtifactMerkleTreeHasher(), - ); - const computedArtifactFunctionTreeRoot = Fr.fromBuffer(computedArtifactFunctionTreeRootBuffer); - const computedArtifactHash = await computeArtifactHash({ - privateFunctionRoot: fn.privateFunctionsArtifactTreeRoot, - utilityFunctionRoot: computedArtifactFunctionTreeRoot, - metadataHash: fn.artifactMetadataHash, - }); - if (!contractClass.artifactHash.equals(computedArtifactHash)) { - log.debug(`Artifact hash mismatch`, { - expected: contractClass.artifactHash, - computedArtifactHash, - computedFunctionArtifactHash: functionArtifactHash, - computedArtifactFunctionTreeRoot, - privateFunctionsArtifactTreeRoot: fn.privateFunctionsArtifactTreeRoot, - metadataHash: fn.artifactMetadataHash, - artifactFunctionTreeSiblingPath: fn.artifactTreeSiblingPath.map(fr => fr.toString()).join(','), - }); - return false; - } - - return true; -} diff --git a/yarn-project/stdlib/src/epoch-helpers/index.ts b/yarn-project/stdlib/src/epoch-helpers/index.ts index 637afa3caf09..0ae35f5461f4 100644 --- a/yarn-project/stdlib/src/epoch-helpers/index.ts +++ b/yarn-project/stdlib/src/epoch-helpers/index.ts @@ -57,6 +57,17 @@ export function getSlotAtTimestamp( : SlotNumber.fromBigInt((ts - constants.l1GenesisTime) / BigInt(constants.slotDuration)); } +/** Returns the timestamp of the next L1 slot boundary after the given wall-clock time. */ +export function getNextL1SlotTimestamp( + nowInSeconds: number, + constants: Pick, +): bigint { + const now = BigInt(nowInSeconds); + const elapsed = now - constants.l1GenesisTime; + const currentL1Slot = elapsed / BigInt(constants.ethereumSlotDuration); + return constants.l1GenesisTime + (currentL1Slot + 1n) * BigInt(constants.ethereumSlotDuration); +} + /** Returns the L2 slot number at the next L1 block based on the current timestamp. */ export function getSlotAtNextL1Block( currentL1Timestamp: bigint, diff --git a/yarn-project/stdlib/src/interfaces/archiver.test.ts b/yarn-project/stdlib/src/interfaces/archiver.test.ts index c996c11d7178..9d1eb6cad1aa 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.test.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.test.ts @@ -289,11 +289,7 @@ describe('ArchiverApiSchema', () => { it('getContractClass', async () => { const contractClass = await getContractClassFromArtifact(artifact); const result = await context.client.getContractClass(Fr.random()); - expect(result).toEqual({ - ...omit(contractClass, 'publicBytecodeCommitment'), - utilityFunctions: [], - privateFunctions: [], - }); + expect(result).toEqual(omit(contractClass, 'publicBytecodeCommitment', 'privateFunctions')); }); it('getDebugFunctionName', async () => { @@ -601,7 +597,7 @@ class MockArchiver implements ArchiverApi { async getContractClass(id: Fr): Promise { expect(id).toBeInstanceOf(Fr); const contractClass = await getContractClassFromArtifact(this.artifact); - return Promise.resolve({ ...contractClass, utilityFunctions: [], privateFunctions: [] }); + return Promise.resolve(contractClass); } async getBytecodeCommitment(id: Fr): Promise { expect(id).toBeInstanceOf(Fr); diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts index 71eef7dca702..e0137bd8c6c6 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts @@ -472,11 +472,7 @@ describe('AztecNodeApiSchema', () => { it('getContractClass', async () => { const contractClass = await getContractClassFromArtifact(artifact); const response = await context.client.getContractClass(Fr.random()); - expect(response).toEqual({ - ...omit(contractClass, 'publicBytecodeCommitment'), - utilityFunctions: [], - privateFunctions: [], - }); + expect(response).toEqual(omit(contractClass, 'publicBytecodeCommitment', 'privateFunctions')); }); it('getContract', async () => { @@ -830,7 +826,7 @@ class MockAztecNode implements AztecNode { async getContractClass(id: Fr): Promise { expect(id).toBeInstanceOf(Fr); const contractClass = await getContractClassFromArtifact(this.artifact); - return { ...contractClass, utilityFunctions: [], privateFunctions: [] }; + return contractClass; } async getContract(address: AztecAddress): Promise { expect(address).toBeInstanceOf(AztecAddress); diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index c33aaa591bd4..14c491fdb772 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -92,10 +92,8 @@ import { type ContractClassPublic, ContractDeploymentData, type ContractInstanceWithAddress, - type ExecutablePrivateFunctionWithMembershipProof, type PrivateFunction, SerializableContractInstance, - type UtilityFunctionWithMembershipProof, computeContractClassId, computePublicBytecodeCommitment, } from '../contract/index.js'; @@ -1183,35 +1181,6 @@ export function makePublicTxBaseRollupPrivateInputs(seed = 0) { }); } -export function makeExecutablePrivateFunctionWithMembershipProof( - seed = 0, -): ExecutablePrivateFunctionWithMembershipProof { - return { - selector: makeSelector(seed), - bytecode: makeBytes(100, seed + 1), - artifactTreeSiblingPath: makeTuple(3, fr, seed + 2), - artifactTreeLeafIndex: seed + 2, - privateFunctionTreeSiblingPath: makeTuple(3, fr, seed + 3), - privateFunctionTreeLeafIndex: seed + 3, - artifactMetadataHash: fr(seed + 4), - functionMetadataHash: fr(seed + 5), - utilityFunctionsTreeRoot: fr(seed + 6), - vkHash: fr(seed + 7), - }; -} - -export function makeUtilityFunctionWithMembershipProof(seed = 0): UtilityFunctionWithMembershipProof { - return { - selector: makeSelector(seed), - bytecode: makeBytes(100, seed + 1), - artifactTreeSiblingPath: makeTuple(3, fr, seed + 2), - artifactTreeLeafIndex: seed + 2, - artifactMetadataHash: fr(seed + 4), - functionMetadataHash: fr(seed + 5), - privateFunctionsArtifactTreeRoot: fr(seed + 6), - }; -} - export async function makeContractClassPublic(seed = 0, publicBytecode?: Buffer): Promise { const artifactHash = fr(seed + 1); const privateFunctionsRoot = fr(seed + 3); @@ -1223,8 +1192,6 @@ export async function makeContractClassPublic(seed = 0, publicBytecode?: Buffer) artifactHash, packedBytecode, privateFunctionsRoot, - privateFunctions: [], - utilityFunctions: [], version: 1, }; } diff --git a/yarn-project/stdlib/src/tx/validator/error_texts.ts b/yarn-project/stdlib/src/tx/validator/error_texts.ts index 6a8326f032a8..bcc100c2d9b0 100644 --- a/yarn-project/stdlib/src/tx/validator/error_texts.ts +++ b/yarn-project/stdlib/src/tx/validator/error_texts.ts @@ -41,5 +41,13 @@ export const TX_ERROR_SIZE_ABOVE_LIMIT = 'Transaction size above size limit'; // Block header export const TX_ERROR_BLOCK_HEADER = 'Block header not found'; +// Contract instance +export const TX_ERROR_INCORRECT_CONTRACT_ADDRESS = 'Incorrect contract instance deployment address'; +export const TX_ERROR_MALFORMED_CONTRACT_INSTANCE_LOG = 'Failed to parse contract instance deployment log'; + +// Contract class +export const TX_ERROR_INCORRECT_CONTRACT_CLASS_ID = 'Incorrect contract class id'; +export const TX_ERROR_MALFORMED_CONTRACT_CLASS_LOG = 'Failed to parse contract class registration log'; + // General export const TX_ERROR_DURING_VALIDATION = 'Unexpected error during validation'; diff --git a/yarn-project/txe/src/state_machine/index.ts b/yarn-project/txe/src/state_machine/index.ts index 5976e9f346a6..ca44362beee2 100644 --- a/yarn-project/txe/src/state_machine/index.ts +++ b/yarn-project/txe/src/state_machine/index.ts @@ -59,6 +59,7 @@ export class TXEStateMachine { new MockEpochCache(), getPackageVersion() ?? '', new TestCircuitVerifier(), + new TestCircuitVerifier(), undefined, log, ); diff --git a/yarn-project/txe/src/util/txe_public_contract_data_source.ts b/yarn-project/txe/src/util/txe_public_contract_data_source.ts index 8573d54ee6c8..1a4c5b36ca88 100644 --- a/yarn-project/txe/src/util/txe_public_contract_data_source.ts +++ b/yarn-project/txe/src/util/txe_public_contract_data_source.ts @@ -26,8 +26,6 @@ export class TXEPublicContractDataSource implements ContractDataSource { packedBytecode: contractClass.packedBytecode, privateFunctionsRoot: contractClass.privateFunctionsRoot, version: contractClass.version, - privateFunctions: [], - utilityFunctions: [], }; } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index a62105bd4c69..5e3e539b3f0f 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -931,6 +931,7 @@ __metadata: commander: "npm:^12.1.0" jest: "npm:^30.0.0" jest-mock-extended: "npm:^4.0.0" + msgpackr: "npm:^1.11.2" pako: "npm:^2.1.0" source-map-support: "npm:^0.5.21" ts-node: "npm:^10.9.1"